探讨:异步与同步的执行差异(结合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 。

浙公网安备 33010602011771号