Skip to content

Commit

Permalink
[WIP][Scheduler] Use rIC to post first callback (#16166)
Browse files Browse the repository at this point in the history
Scheduler uses `requestAnimationFrame` to post tasks to the browser.
If this happens at the beginning of a frame, the callback might not
fire until the subsequent frame, even if the main thread is idle.

Our theory was that this wouldn't be an issue in practice, because once
the first rAF fires, we schedule the next rAF as early as possible in
that frame. Combined with our heuristic for computing frame deadlines,
we shouldn't get any idle time in between frames — only before the
*first* frame.

This reasoning holds true when you have a small number of large tasks,
such as the ones posted by React. The idle time before the task starts
is negligible relative to the lifetime of the entire task.

However, it does not hold if you have many small tasks posted over a
span of time, perhaps spawned by a flurry of IO events. In this case,
instead of single, contiguous rAF loop preceded by an idle frame, you
get many rAF loops preceded by many idle frames. Our new theory is that
this is contributing to poor main thread utilization during page loads.

To try to reclaim as much idle time as possible, this PR adds two
experimental flags. The first one adds a `requestIdleCallback` call to
start the rAF loop, which will fire before rAF if there's idle time left
in the frame. (We still call rAF in case there isn't. We start working
in whichever event fires first.)

The second flag tries a similar strategy using `setTimeout(fn, 0)`. If
the timer fires before rAF, we'll assume that the main thread is idle.

If either `requestIdleCallback` or `setTimeout` fires before rAF, we'll
immediately peform some work. Since we don't have a real frame time that
we can use to compute the frame deadline, we'll do an entire frame
length of work. This will probably push us past the vsync, but this is
fine because we can catch up during the next frame, by which point a
real rAF will have fired and the loop can proceed the same way it
does today.

Test plan: Try this on Facebook to see if it improves load times
  • Loading branch information
acdlite authored Jul 22, 2019
1 parent 2bd88e3 commit 3f2cafe
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 141 deletions.
2 changes: 2 additions & 0 deletions packages/scheduler/src/SchedulerFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@

export const enableSchedulerDebugging = false;
export const enableIsInputPending = false;
export const requestIdleCallbackBeforeFirstFrame = false;
export const requestTimerEventBeforeFirstFrame = false;
2 changes: 2 additions & 0 deletions packages/scheduler/src/forks/SchedulerFeatureFlags.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
export const {
enableIsInputPending,
enableSchedulerDebugging,
requestIdleCallbackBeforeFirstFrame,
requestTimerEventBeforeFirstFrame,
} = require('SchedulerFeatureFlags');
Loading

0 comments on commit 3f2cafe

Please sign in to comment.