React: Under what circumstances, unstable_shouldYield will return true?

Created on 8 Feb 2019  ·  4Comments  ·  Source: facebook/react

In Scheduler.js,

function unstable_shouldYield() {
  return (
    !currentDidTimeout &&
    ((firstCallbackNode !== null &&
      firstCallbackNode.expirationTime < currentExpirationTime) ||
      shouldYieldToHost())
  );
}

unstable_shouldYield() return true when currentDidTimeout is false and shouldYieldToHost() return true, but why?

shouldYieldToHost = function() {
  return frameDeadline <= getCurrentTime();
};

shouldYieldToHost() return true means there's no time left in this idle period
currentDidTimeout is false means the schedule is not timeout
what relationship between them, why does unstable_shouldYield() depend on them?

Question

Most helpful comment

We yield to the host environment periodically – every 16ms or so – to allow the browse to process incoming events including user input. frameDeadline is the timestamp that we plan to yield at (originally set to something like now() + 16ms), so shouldYieldToHost returns true once that time has passed. Then we use some combination of requestIdleCallback and requestAnimationFrame so we can process the next piece of work soon.

In the ideal case, we can finish all of the rendering in these small 16ms slices. However, if there are many other things happening at the same time, React work may "starve" and not be able to fully render in the small slices. So we have a second check: every pending render or state update has an "expiration time" (usually somewhere from 100ms to 5000ms) – if that time passes without the render finishing, we switch to a synchronous mode until that update can be finished. This is not ideal but it ensures that all updates get processed without waiting too long.

We set a timer in the browser (e.g., with setTimeout) for that same expiration time. If that timer fires, we know we need to perform the work synchronously. If this happens, currentDidTimeout is set to true, so we won't yield.

In the future, we plan to use a new isInputPending browser API (https://github.com/WICG/is-input-pending) so we can continue processing work and only yield when there is new user input, instead of always yielding every 16ms.

All 4 comments

We yield to the host environment periodically – every 16ms or so – to allow the browse to process incoming events including user input. frameDeadline is the timestamp that we plan to yield at (originally set to something like now() + 16ms), so shouldYieldToHost returns true once that time has passed. Then we use some combination of requestIdleCallback and requestAnimationFrame so we can process the next piece of work soon.

In the ideal case, we can finish all of the rendering in these small 16ms slices. However, if there are many other things happening at the same time, React work may "starve" and not be able to fully render in the small slices. So we have a second check: every pending render or state update has an "expiration time" (usually somewhere from 100ms to 5000ms) – if that time passes without the render finishing, we switch to a synchronous mode until that update can be finished. This is not ideal but it ensures that all updates get processed without waiting too long.

We set a timer in the browser (e.g., with setTimeout) for that same expiration time. If that timer fires, we know we need to perform the work synchronously. If this happens, currentDidTimeout is set to true, so we won't yield.

In the future, we plan to use a new isInputPending browser API (https://github.com/WICG/is-input-pending) so we can continue processing work and only yield when there is new user input, instead of always yielding every 16ms.

Thanks for reply
I still have some questions,

  1. I think unstable_shouldYield() represent for whether the work can be interrupted or not in the schedule,is that correct?
  2. Does the "16ms" refer to activeFrameTime?
  3. When will firstCallbackNode.expirationTime < currentExpirationTime return true ? Does that mean the next work's priority is higher than the previous one?
  1. Each rendering task should check unstable_shouldYield() frequently. (We call it roughly after each component in the tree.) Most of the time it will return false (which means keep going) but when it returns true that means the rendering needs to pause.

  2. Yes.

  3. I believe that condition is true if some newer, higher-priority work is enqueued during an existing render. In that case, we want to return true so that we can switch to that task.

Thank you very much.It's really nice of you!

Was this page helpful?
0 / 5 - 0 ratings