1. React版本
  2. Component.prototype.setState
    1. this.updater.enqueueSetState
      1. getInstance
      2. createUpdate
      3. enqueueUpdate
        1. createUpdateQueue
        2. appendUpdateToQueue
      4. scheduleWork
        1. scheduleWorkToRoot
        2. recordScheduleUpdate
        3. resetStack
        4. requestWork
          1. scheduleCallbackWithExpirationTime
  3. 总结
  4. 参考资料

React版本

16.8.6

Component.prototype.setState

react/packages/react/src/ReactBaseClasses.js

Component.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',
  );
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

this.updater.enqueueSetState

react/packages/reactreconciler/src/ReactFiberClassComponent.js

如果搜索这个函数名的话,你会发现有四个地方,三个是定义,一个是使用,另外两个定义的地方分别是用来测试和报错

enqueueSetState(inst, payload, callback) {
  const fiber = getInstance(inst);  //获得组件实例
  const currentTime = requestCurrentTime();
  const expirationTime = computeExpirationForFiber(currentTime, fiber);

  const update = createUpdate(expirationTime);
  update.payload = payload;
  if (callback !== undefined && callback !== null) {
    if (__DEV__) {
      warnOnInvalidCallback(callback, 'setState');
    }
    update.callback = callback;
  }

  //flushPassiveEffects和scheduleWork都会根据以下路径的boolean常量(false)进行不同的函数赋值
  //应该是控制新旧版本逻辑的代码,可能未来返回true,使用新的调度逻辑
  //react/packages/shared/ReactFeatureFlags.js
  flushPassiveEffects();  //好像没什么意义,最终返回了一个boolean,也没人承接
  enqueueUpdate(fiber, update);
  scheduleWork(fiber, expirationTime);
}

getInstance

react/packages/shared/ReactInstanceMap.js

此处是getInstance,使用了as关键字进行了重命名

export function get(key) {
  return key._reactInternalFiber;
}

createUpdate

react/packages/react-reconciler/src/ReactUpdateQueue.js

export function createUpdate(expirationTime: ExpirationTime): Update<*> {
  return {
    expirationTime: expirationTime,

    tag: UpdateState,
    payload: null,
    callback: null,
    next: null,
    nextEffect: null,
  };
}

enqueueUpdate

react/packages/react-reconciler/src/ReactUpdateQueue.js

export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  const alternate = fiber.alternate;  //获取fiber替身,就是即将要更新的fiber对象
  let queue1;
  let queue2;
  //下面是对queue1、queue2的空值处理
  if (alternate === null) {  //首次更新
    queue1 = fiber.updateQueue;
    queue2 = null;
    if (queue1 === null) {
      queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
    }
  } else {  //非首次更新
    queue1 = fiber.updateQueue;
    queue2 = alternate.updateQueue;
    if (queue1 === null) {
      if (queue2 === null) {
        queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
        queue2 = alternate.updateQueue = createUpdateQueue(
          alternate.memoizedState,
        );
      } else {
        queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
      }
    } else {
      if (queue2 === null) {
        queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
      } else {
        // Both owners have an update queue.
      }
    }
  }
  //下面添加队列逻辑
  if (queue2 === null || queue1 === queue2) {  //只有一种queue的情况
    appendUpdateToQueue(queue1, update);
  } else {  //当有两个queue时合并,因为我们不想让相同的queue多次添加
    if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
      //只有一种lastUpdate的情况,将lastUpdate添加到两个queue
      appendUpdateToQueue(queue1, update);
      appendUpdateToQueue(queue2, update);
    } else {
      // Both queues are non-empty. The last update is the same in both lists,
      // because of structural sharing. So, only append to one of the lists.
      appendUpdateToQueue(queue1, update);
      //修正queue2.lastUpdate指向
      queue2.lastUpdate = update;
    }
  }

  if (__DEV__) {
    if (
      fiber.tag === ClassComponent &&
      (currentlyProcessingQueue === queue1 ||
        (queue2 !== null && currentlyProcessingQueue === queue2)) &&
      !didWarnUpdateInsideUpdate
    ) {
      warningWithoutStack(
        false,
        'An update (setState, replaceState, or forceUpdate) was scheduled ' +
          'from inside an update function. Update functions should be pure, ' +
          'with zero side-effects. Consider using componentDidUpdate or a ' +
          'callback.',
      );
      didWarnUpdateInsideUpdate = true;
    }
  }
}
createUpdateQueue

react/packages/react-reconciler/src/ReactUpdateQueue.js

export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
  const queue: UpdateQueue<State> = {
    baseState,
    firstUpdate: null,  //链表头
    lastUpdate: null,  //链表尾
    firstCapturedUpdate: null,
    lastCapturedUpdate: null,
    firstEffect: null,
    lastEffect: null,
    firstCapturedEffect: null,
    lastCapturedEffect: null,
  };
  return queue;
}
appendUpdateToQueue

react/packages/react-reconciler/src/ReactUpdateQueue.js

function appendUpdateToQueue<State>(
  queue: UpdateQueue<State>,
  update: Update<State>,
) {
  //链表插入逻辑
  if (queue.lastUpdate === null) {
    queue.firstUpdate = queue.lastUpdate = update;
  } else {
    queue.lastUpdate.next = update;
    queue.lastUpdate = update;
  }
}

scheduleWork

react/packages/react-reconciler/src/ReactFiberScheduler.old.js

function scheduleWork (fiber: Fiber, expirationTime: ExpirationTime) {
  const root = scheduleWorkToRoot(fiber, expirationTime);  //获取 fiber root
  if (root === null) {
    if (__DEV__) {
      switch (fiber.tag) {
        case ClassComponent:
          warnAboutUpdateOnUnmounted(fiber, true);
          break;
        case FunctionComponent:
        case ForwardRef:
        case MemoComponent:
        case SimpleMemoComponent:
          warnAboutUpdateOnUnmounted(fiber, false);
          break;
      }
    }
    return;
  }
  if (
    !isWorking &&  //如果不处于任务状态
    nextRenderExpirationTime !== NoWork &&  //从词义可以看出来:下一个渲染过期时间段有任务
    expirationTime > nextRenderExpirationTime  //比较优先级
  ) {
    interruptedBy = fiber;  //记录打断者
    resetStack();  //重置fiber栈
  }
  markPendingPriorityLevel(root, expirationTime);  //标记等待任务优先级
  if (
    // 如果我们在执行任务状态,我们不需要为了更新调度这个fiber任务,因为我们会在退出之前执行它,除非两者(正在渲染的和等待的)的fiber root不相同
    !isWorking ||
    isCommitting ||
    nextRoot !== root
  ) {
    const rootExpirationTime = root.expirationTime;
    requestWork(root, rootExpirationTime);  //请求执行任务
  }
    //触发无限循环逻辑警告
  if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
    // Reset this back to zero so subsequent updates don't throw.
    nestedUpdateCount = 0;
    invariant(
      false,
      'Maximum update depth exceeded. This can happen when a ' +
        'component repeatedly calls setState inside ' +
        'componentWillUpdate or componentDidUpdate. React limits ' +
        'the number of nested updates to prevent infinite loops.',
    );
  }
  if (__DEV__) {
    if (
      isInPassiveEffectDEV &&
      nestedPassiveEffectCountDEV > NESTED_PASSIVE_UPDATE_LIMIT
    ) {
      nestedPassiveEffectCountDEV = 0;
      warning(
        false,
        'Maximum update depth exceeded. This can happen when a ' +
          'component calls setState inside useEffect, but ' +
          "useEffect either doesn't have a dependency array, or " +
          'one of the dependencies changes on every render.',
      );
    }
  }
}
scheduleWorkToRoot

react/packages/react-reconciler/src/ReactFiberScheduler.old.js

function scheduleWorkToRoot(fiber: Fiber, expirationTime): FiberRoot | null {
  recordScheduleUpdate();  //记录调度器的状态

  if (__DEV__) {
    if (fiber.tag === ClassComponent) {
      const instance = fiber.stateNode;
      warnAboutInvalidUpdates(instance);
    }
  }

  // expirationTime 越大说明优先级越高
  if (fiber.expirationTime < expirationTime) {
    fiber.expirationTime = expirationTime;
  }
  let alternate = fiber.alternate;
  if (alternate !== null && alternate.expirationTime < expirationTime) {
    alternate.expirationTime = expirationTime;
  }
  // Walk the parent path to the root and update the child expiration time.
  // 获取 fiber 的父节点
  let node = fiber.return;
  let root = null;
  // 判断这时候 fiber 是否为 Root Fiber
  if (node === null && fiber.tag === HostRoot) {
    // 取出 Fiber Root
    root = fiber.stateNode;
  } else {
    while (node !== null) {  //Root Fiber 是没有 return 属性的
      alternate = node.alternate;
      //调整优先级
      if (node.childExpirationTime < expirationTime) {
        node.childExpirationTime = expirationTime;
        if (
          alternate !== null &&
          alternate.childExpirationTime < expirationTime
        ) {
          alternate.childExpirationTime = expirationTime;
        }
      } else if (
        alternate !== null &&
        alternate.childExpirationTime < expirationTime
      ) {
        alternate.childExpirationTime = expirationTime;
      }
      if (node.return === null && node.tag === HostRoot) {
        root = node.stateNode;
        break;
      }
      node = node.return;
    }
  }

  // 这部分内容是 React Profiler 相关的,DevTool 相关的内容
  // 可以方便找出你的 React 应用的性能瓶颈
  if (enableSchedulerTracing) {
    if (root !== null) {
      const interactions = __interactionsRef.current;
      if (interactions.size > 0) {
        const pendingInteractionMap = root.pendingInteractionMap;
        const pendingInteractions = pendingInteractionMap.get(expirationTime);
        if (pendingInteractions != null) {
          interactions.forEach(interaction => {
            if (!pendingInteractions.has(interaction)) {
              // Update the pending async work count for previously unscheduled interaction.
              interaction.__count++;
            }

            pendingInteractions.add(interaction);
          });
        } else {
          pendingInteractionMap.set(expirationTime, new Set(interactions));

          // Update the pending async work count for the current interactions.
          interactions.forEach(interaction => {
            interaction.__count++;
          });
        }

        const subscriber = __subscriberRef.current;
        if (subscriber !== null) {
          const threadID = computeThreadID(
            expirationTime,
            root.interactionThreadID,
          );
          subscriber.onWorkScheduled(interactions, threadID);
        }
      }
    }
  }
  return root;
}
recordScheduleUpdate

react/packages/react-reconciler/src/ReactDebugFiberPerf.js

export function recordScheduleUpdate(): void {
  if (enableUserTimingAPI) {
    if (isCommitting) {
      hasScheduledUpdateInCurrentCommit = true;
    }
    if (
      currentPhase !== null &&
      currentPhase !== 'componentWillMount' &&
      currentPhase !== 'componentWillReceiveProps'
    ) {
      hasScheduledUpdateInCurrentPhase = true;
    }
  }
}
resetStack

react/packages/react-reconciler/src/ReactFiberScheduler.old.js

function resetStack() {
  if (nextUnitOfWork !== null) {
    let interruptedWork = nextUnitOfWork.return;  //向上查找节点
    while (interruptedWork !== null) {
      unwindInterruptedWork(interruptedWork);  //还原已经改变的节点,这里不再展开,是一系列的还原逻辑
      interruptedWork = interruptedWork.return;
    }
  }

  if (__DEV__) {
    ReactStrictModeWarnings.discardPendingWarnings();
    checkThatStackIsEmpty();
  }
  // 重置变量
  nextRoot = null;
  nextRenderExpirationTime = NoWork;
  nextLatestAbsoluteTimeoutMs = -1;
  nextRenderDidError = false;
  nextUnitOfWork = null;
}
requestWork

react/packages/react-reconciler/src/ReactFiberScheduler.old.js

这里是主要内容

function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
  // 将fiber root加入调度中
  addRootToSchedule(root, expirationTime);
  if (isRendering) {
    //阻止重复渲染,剩余的任务将会在当前渲染阶段结束调度
    return;
  }
  // 以下判断,执行的更新任务会包一层batchedUpdates函数,执行时改变下面判断变量的值,进而判断任务是否需要批量更新
  // 判断是否需要批量更新
  if (isBatchingUpdates) {
    // 判断是否不需要批量更新
    if (isUnbatchingUpdates) {
      // ...unless we're inside unbatchedUpdates, in which case we should
      // flush it now.
      // 除非我们还未在里面批量更新,这时我们应该立即批量更新它,说实话我不太懂这种是什么情况
      nextFlushedRoot = root;
      nextFlushedExpirationTime = Sync;
      performWorkOnRoot(root, Sync, false);
    }
    return;  //这里是为什么我们的setState没有立即更新状态的原因
  }

  // 不需要批量更新的逻辑
  // TODO: Get rid of Sync and use current time?
  // 以上TODO还未实现
  // 判断优先级是同步还是异步,异步的话需要调度
  if (expirationTime === Sync) {
    performSyncWork();  //执行同步任务
  } else {
    // 因为setTimeOut等类似函数属于异步,所以执行这个分支的函数
    // 函数核心是实现了 requestIdleCallback 的 polyfill 版本
    // 因为这个函数浏览器的兼容性很差
    // 具体作用可以查看 MDN 文档 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback
    // 这个函数可以让浏览器空闲时期依次调用函数,这就可以让开发者在主事件循环中执行后台或低优先级的任务,
    // 而且不会对像动画和用户交互这样延迟敏感的事件产生影响
    scheduleCallbackWithExpirationTime(root, expirationTime);
  }
}
scheduleCallbackWithExpirationTime

react/packages/react-reconciler/src/ReactFiberScheduler.old.js

function scheduleCallbackWithExpirationTime(
  root: FiberRoot,
  expirationTime: ExpirationTime,
) {
  // 判断上一个 callback 是否执行完毕
  if (callbackExpirationTime !== NoWork) {
    if (expirationTime < callbackExpirationTime) {
      return;  // 当前任务如果优先级小于上个任务就退出
    } else {
      if (callbackID !== null) {
        cancelCallback(callbackID);  // 否则的话就取消上个 callback
      }
    }
    // The request callback timer is already running. Don't start a new one.
  } else {
    // 没有需要执行的上一个 callback,开始定时器,这个函数用于 devtool
    startRequestCallbackTimer();
  }

  callbackExpirationTime = expirationTime;
  // 当前 performance.now() 和程序刚执行时的 performance.now() 相减
  const currentMs = now() - originalStartTimeMs;
  const expirationTimeMs = expirationTimeToMs(expirationTime);
  // 毫秒相减,优先级越大,算出来的值越小,可以自己模拟一下
  // 这个值在后面会和 performance.now() 相加再算出一个 expirationTime
  const timeout = expirationTimeMs - currentMs;
  // 获取优先级,这个优先级有五个,但是这个优先级其实在 scheduleCallback 函数中压根没用到
  const priorityLevel = getCurrentPriorityLevel();
  callbackID = scheduleCallback(priorityLevel, performAsyncWork, {timeout});
}

总结

可以看出来,setState本身乃至本身做的事情都是同步的,只是将任务加入队列,并根据react类型判断是否需要批量更新,而使用setTimeout这种api会判断为异步类型导致立即执行任务。

参考资料

https://github.com/KieSun/react-interpretation

https://github.com/acdlite/react-fiber-architecture