结论:react v18之后,不管在哪里调用都是异步
在React18之前:setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的
setState的异步
1、setState其本身的执行代码是同步的,只是合成事件和钩子函数的调用顺序在更新(批量更新)之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”。
2、setState的异步是合并逻辑导致的。
在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新 this.state还是放到一个updateQueue中延时更新,而 isBatchingUpdates默认是false,表示setState会同步更新this.state;但是,有一个函数batchedUpdates,该函数会把 isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState不会同步更新this.state,而是异步的。
3、如果 setState 在 React 能够控制的范围被调用,它就是异步的。
例如:合成事件处理函数, 生命周期函数, 此时会进行批量更新, 也就是将状态合并后再进行 DOM 更新。
4、如果 setState 在原生 JavaScript 控制的范围被调用,它就是同步的。
例如:原生事件处理函数中, 定时器回调函数中, Ajax 回调函数中, 此时 setState 被调用后会立即更新 DOM 。
5、在异步中:
对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果
如果是同时 setState 多个不同的值,在更新时会对其进行合并后进行批量更新。
setState 异步的一个重要的动机——避免频繁的 re-render。