Skip to content

React setState 同步/异步 #37

Description

@scarcoco

setState

我测试的 React 的版本为 16.13.0

setState 同步异步是和执行上下文有关系,setState -> this.updater.enqueueSetState -> scheduleUpdateOnFiber,而函数 scheduleUpdateOnFiber 的关键逻辑如下,能看到什么时候同步和异步。

同步的条件:

  • 第一个条件 LegacyUnbatchedContext 没有太明白
  • executionContext === NoContext,即无法获取执行上下文,比如 setTimeout、原生事件等;
  • setState 同步更新,那么更新的时机是什么时候呢?
    • dispatchDiscreteEvent 之前会把队列中处理掉;
    • dispatchDiscreteEvent 之后,也会把队列处理掉;
    • 其他还很多情况会调用 flushSyncCallbackQueue,大部分是 executionContext === NoContext 或者主动调用;
if (expirationTime === Sync) {
    if ( // Check if we're inside unbatchedUpdates
        (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering
        (executionContext & (RenderContext | CommitContext)) === NoContext) {
        // Register pending interactions on the root to avoid losing traced interaction data.
        schedulePendingInteractions(root, expirationTime); // This is a legacy edge case. The initial mount of a ReactDOM.render-ed
        // root inside of batchedUpdates should be synchronous, but layout updates
        // should be deferred until the end of the batch.

        performSyncWorkOnRoot(root);
    } else {
        ensureRootIsScheduled(root);
        schedulePendingInteractions(root, expirationTime);

        if (executionContext === NoContext) {
            // Flush the synchronous work now, unless we're already working or inside
            // a batch. This is intentionally inside scheduleUpdateOnFiber instead of
            // scheduleCallbackForFiber to preserve the ability to schedule a callback
            // without immediately flushing it. We only do this for user-initiated
            // updates, to preserve historical behavior of legacy mode.
            flushSyncCallbackQueue();
        }
    }
} else {
    ensureRootIsScheduled(root);
    schedulePendingInteractions(root, expirationTime);
}

// Only updates at user-blocking priority or greater are considered
if ((executionContext & DiscreteEventContext) !== NoContext && ( 
    // discrete, even inside a discrete event.
    priorityLevel === UserBlockingPriority$1 || priorityLevel === ImmediatePriority)) {
    // This is the result of a discrete event. Track the lowest priority
    // discrete update per root so we can flush them early, if needed.
    if (rootsWithPendingDiscreteUpdates === null) {
        rootsWithPendingDiscreteUpdates = new Map([[root, expirationTime]]);
    } else {
        var lastDiscreteTime = rootsWithPendingDiscreteUpdates.get(root);

        if (lastDiscreteTime === undefined || lastDiscreteTime > expirationTime) {
            rootsWithPendingDiscreteUpdates.set(root, expirationTime);
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions