龙岗汤坑社区网站建设,做网站排版,在线短网址缩短工具,社区推广活动方案本文内容均针对于18.x以下版本
setState 到底是同步还是异步#xff1f;很多人可能都有这种经历#xff0c;面试的时候面试官给了你一段代码#xff0c;让你说出输出的内容#xff0c;比如这样#xff1a;
constructor(props) {super(props);this.state {val: 0}}compo…本文内容均针对于18.x以下版本
setState 到底是同步还是异步很多人可能都有这种经历面试的时候面试官给了你一段代码让你说出输出的内容比如这样
constructor(props) {super(props);this.state {val: 0}}componentDidMount() {this.setState({data: 1})console.log(val: , this.state.val);// val: 0setTimeout(() {this.setState({data: 2})console.log(setTimeout , this.state.val);//val:2})} 而这段代码的输出结果第一个 console.log 会输出0 而第二个 console.log 会输出2 。也就是第一次 setState 的时候它是异步的第二次 setState 的时候它又变成了同步的。
这是为什么呢 只要你进入了 react 的调度流程那就是异步的。只要你没有进入 react 的调度流程那就是同步的。什么东西不会进入 react 的调度流程 setTimeout setInterval 直接在 DOM 上绑定原生事件等。这些都不会走 React 的调度流程你在这种情况下调用 setState 那这次 setState 就是同步的。 否则就是异步的。 而 setState 同步执行的情况下 DOM 也会被同步更新也就意味着如果你多次 setState 会导致多次更新这是毫无意义并且浪费性能的。 setState 被调用后最终会走到 scheduleUpdateOnFiber 这个函数里面来下面是源码
function scheduleUpdateOnFiber(fiber, expirationTime) {checkForNestedUpdates();warnAboutRenderPhaseUpdatesInDEV(fiber);var root markUpdateTimeFromFiberToRoot(fiber, expirationTime);if (root null) {warnAboutUpdateOnUnmountedFiberInDEV(fiber);return;}checkForInterruption(fiber, expirationTime);recordScheduleUpdate(); // TODO: computeExpirationForFiber also reads the priority. Pass the// priority as an argument to that function and this one.var priorityLevel getCurrentPriorityLevel();if (expirationTime Sync) {if ( // Check if were inside unbatchedUpdates(executionContext LegacyUnbatchedContext) ! NoContext // Check if were 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 were 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);}if ((executionContext DiscreteEventContext) ! NoContext ( // Only updates at user-blocking priority or greater are considered// 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);}}}
}我们着重看这段代码
if (executionContext NoContext) {// Flush the synchronous work now, unless were 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();
}executionContext 代表了目前 react 所处的阶段而 NoContext 你可以理解为是 react 已经没活干了的状态。而 flushSyncCallbackQueue 里面就会去同步调用我们的 this.setState 也就是说会同步更新我们的 state 。所以我们知道了当 executionContext 为 NoContext 的时候我们的 setState 就是同步的。那什么地方会改变 executionContext 的值呢
我们随便找几个地方看看
function batchedEventUpdates$1(fn, a) {var prevExecutionContext executionContext;executionContext | EventContext;...省略
}function batchedUpdates$1(fn, a) {var prevExecutionContext executionContext;executionContext | BatchedContext;...省略
}当 react 进入它自己的调度步骤时会给这个 executionContext 赋予不同的值表示不同的操作以及当前所处的状态而 executionContext 的初始值就是 NoContext 所以只要你不进入 react 的调度流程这个值就是 NoContext 那你的 setState 就是同步的。