探讨:异步与同步的执行差异(结合async await 与 setState)
async await 与 setState 类似,在主线程执行的时候,遇到异步任务总是先挂载起来,一边执行一边等待主线程上的同步任务执行完毕,等同步任务执行完毕,再发送信号给挂载起来的任务,说你可以进主线程来执行了,此时挂载起来的异步任务中谁先执行完毕谁就先去主线程上执行
而两者不同的点在于:遇到 async await 的时候,会先返回(把任务挂载),等同步任务执行完毕,再根据挂载任务是否返回值决定是否去主线程执行,然而,async await 在挂载时,跟在其后的语句也会跟着停止执行,只有等 await 返回值,其后的语句才能执行;setState 在挂载时,并不影响其后语句的执行,也就是说当遇到 setState 时,会将 setState 挂载起来,转而去执行其后的同步语句,等 setState 执行完毕,那么再放入主线程执行。如下所示:
import React,{useState, useEffect} from 'react' import { withRouter } from 'umi'; import { connect } from 'dva'; import decode from 'jwt-decode'; import { Row, Col, Spin, } from 'antd' import Header from '@/containers/Header' import SideBar from '@/containers/SideBar' import DepRecMedicineContainer from '@/containers/DepRecMedicine' function Index (props) { //... const [tokenUserInfo, setTokenUserInfo] = useState([]) const [isReturn, setIsReturn] = useState(false) const verify = async ()=>{ const { dispatch } = props console.log('1') // 日志1 await dispatch({ type: 'GetData/getToken', callback: (data) => { setTokenUserInfo(data) console.log('2') // 日志2 if(data.status === '1'){ const token = localStorage.getItem('@#@TOKEN') const tk = decode(token) // console.log('token/index',token) if(token){ try{ dispatch({ type: 'GetData/saveData', payload: {token: tk} }) }catch{ // 报错 localStorage.removeItem("@#@TOKEN") window.location.href = '/login' } } }else{ localStorage.removeItem("@#@TOKEN") window.location.href = '/login' } } }) console.log('3') // 日志3 } useEffect(() => { verify() console.log('4') // 日志4 setIsReturn(true) console.log('5') // 日志5 },[]) // 指定空数组,只在组件挂载时(刷新)执行一次 const {location} = props console.log('是render内的isReturn',isReturn) // 打印isReturn if(isReturn == true && tokenUserInfo.status == '1'){ if(location.pathname === '/'){ return( <div style={{height:'100vh', overflow: 'hidden'}}> {/* header */} <Row> <Header /> </Row> {/* body */} <Row> <Col span={3}> <SideBar // onClickDepRec={onClickDepRec} // onClickAdmitPatientList={onClickAdmitPatientList} // onClickLeavePatientList={onClickLeavePatientList} // onClickToChangeRightContent={onClickToChangeRightContent} /> </Col> <DepRecMedicineContainer /> </Row> </div> ) }else{ return( <> {props.children} </> ) } }else{ return <Spin /> } } export default connect(({GetData})=>({GetData}))(withRouter(Index))
注意:第一次 render 为 DOM 挂载时 isReturn 的初始值的渲染,第二、三、四次 render 为 setIsReturn(true) 执行完毕后渲染的,第五次 render 为 setTokenUserInfo(data) 执行完毕后渲染的,最后一次 render 为 useEffect() 执行完毕后渲染的
细节:下图中为什么不是先输出 2 和 6 然后再渲染?因为执行到 setTokenUserInfo(data) 时,为异步任务,挂载起来,转而去执行其后语句,因而输出 2 ,然而为什么不输出 6 呢,因为输出 2 之后再接着往下执行同步语句时,挂载起来的 setTokenUserInfo(data) 说他已经执行完毕了,因而回到主线程进行渲染(每 setState 一次就渲染一次!),然后再去接着执行后面的语句,所以输出 6 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构