React中await是否阻塞Props变更渲染?单线程下为何不锁组件?
React组件中await与渲染的关系疑问
假设一个React组件的label状态用于设置<p>标签文本,点击按钮调用含5秒timeout的async函数后,在timeout未结束时点击修改label的按钮,<p>内容会立即更新还是要等timeout完成?换句话说,组件在存在活跃await时会不会被锁定?
示例代码
import { Component } from "react"; export default class TestAsyncComponent extends Component { constructor(props) { super(props); this.state = { label: "BEFORE", }; } startAsync = async () => { console.log("START ASYNC"); console.log(await this.foo()); }; foo = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve("PROMISE FINISHED"); }, 5000); }); }; changeLabel = () => { this.setState({ label: "AFTER" }, () => { console.log("LABEL CHANGED"); }); }; render() { return ( <div> <p>{this.state.label}</p> <button style={{ width: "100px", height: "50px" }} onClick={this.startAsync} > {"START ASYNC"} </button> <button style={{ width: "100px", height: "50px" }} onClick={this.changeLabel} > {"CHANGE LABEL"} </button> </div> ); } }
实际测试发现,点击START ASYNC后立刻点CHANGE LABEL,label会直接变更。疑问点:既然JavaScript是单线程,await是怎么处理的,为啥不会锁定组件?
解答
核心原因在于JavaScript的单线程是基于事件循环的,await并不会阻塞整个线程,只是暂停当前async函数的执行而已。
具体拆解整个过程:
- 点击
START ASYNC,startAsync函数执行,先打印"START ASYNC",调用this.foo()返回一个Promise;遇到await时,JS引擎暂停startAsync的执行,把await后的代码(console.log(...))放到微任务队列,然后让出线程处理其他事件。 - 此时点击
CHANGE LABEL,changeLabel触发setState更新label状态。React检测到状态变化后,立即调度组件重新渲染,这个渲染任务会进入事件循环的任务队列,线程空闲时就会执行渲染,所以<p>内容立刻变成"AFTER"。 - 5秒后,
setTimeout的回调触发,将Promise标记为resolved;之前放入微任务队列的代码(startAsync中await后面的部分)会被执行,打印"PROMISE FINISHED"。
直白说:await只是暂停当前async函数的执行流程,不会卡住整个JS线程,其他用户交互、React渲染这类事件该处理还是会处理,所以组件根本不会被锁定。
内容的提问来源于stack exchange,提问作者LilLakes
相关产品推荐
相关产品推荐

