setState 是同步还是异步的?

class App extends React.Component {
state = {
num: 0
};
analyze = () => {
this.setState({
num: this.state.num + 1
});
console.log("num", this.state.num);
this.setState({
num: this.state.num + 1
});
console.log("num", this.state.num);
this.setState({
num: this.state.num + 1
});
console.log("num", this.state.num);
};
handleClick = () => {
this.analyze(); // ①
// setTimeout(() => { // ②
// this.analyze();
// }, 0);
};
render() {
return <button onClick={this.handleClick}>click button</button>;
}
}

① 处代码打印结果: 0 0 0; ② 处代码打印结果: 1 2 3;

demo 演示

为什么会出现这样的现象呢, 相关源码如下:

// 如果满足一定条件, 则对 setState 做批量更新
function batchedUpdates(fn) {
const previousIsBatchingUpdates = isBatchingUpdates;
isBatchingUpdates = true;
try {
return fn(a); // 相当于所有 setState 都在这里执行, 相当于就是异步执行 setState 了。可见上述 demo
} finally {
isBatchingUpdates = previousIsBatchingUpdates;
if (!isBatchingUpdates && !isRendering) {
performSyncWork();
}
}
}
// setState 会进这个函数
function requestWork(root, expirationTime) {
addRootToSchedule(root, expirationTime);
if (isRendering) {
return;
}
if (isBatchingUpdates) {
// Flush work at the end of the batch.
if (isUnbatchingUpdates) {
// ...unless we're inside unbatchedUpdates, in which case we should
// flush it now.
nextFlushedRoot = root;
nextFlushedExpirationTime = Sync;
performWorkOnRoot(root, Sync, false);
}
return;
}
// 如果 isBatchingUpdates 为 false 则执行这里, 相当于 setState 每次调用这里, 就是同步的了
if (expirationTime === Sync) {
performSyncWork();
} else {
scheduleCallbackWithExpirationTime(root, expirationTime);
}
}

依赖上下文中 isBatchingUpdates 等相关布尔值, 如果 isBatchingUpdatestrue 则进行批量更新, 如果 isBatchingUpdatesfalse 则进行同步更新