详细解释vue中异步等待的误解
我看到了对钩子函数使用async/await,以便使钩子函数的异步代码同步执行,就像下面的代码一样:
//exp-01 export default { async created(){ const TiME KeY=' cost ';console . time(TiME key);console . log(' start created ');this . list=wait this . getlist();console . log(this . list);console . log(' end created ');console . timeend(time key);},mounted(){ const TiME key=' cost ';console . time(TiME key);console.log('开始挂载');console . log(this . list . rows);console.log('末端安装');console . timeend(time key);},data(){ return { list :[]};},methods: { getList() {返回新的Promise((resolve)={ setTimeout(()={返回resolve({ row :[{ name : ' is AAC ',position : ' coder ' }]});}, 3000);});} }};最终将输出exp-01的代码:
start created start mounteddendefinended mountedded cost 2.88623046875 ms { _ _ ob _ _ : Observer } end created cost : 33363636365
显然,没有达到预期的效果。为什么呢?
根据exp-01的输出结果,我们可以看到代码的执行顺序,首先是钩子的执行顺序:
已创建=已安装
是的,钩子的执行顺序正常,没有被打扰。证据是,创建的钩子中的同步代码是在挂载之前执行的:
启动已安装的createdstart查看创建的钩子内部的异步代码:
this . list=wait this . getlist();
你可以看到这个列表的打印结果
末端贴装成本: 2.88623046875 ms//就是这个。由创建的钩子打印的列表{_ _ ob _ _ : observer}。这意味着使用async/await的钩子内部的异步代码不会阻止钩子主线程的执行。这里钩子功能的主线是指:
before create=created=before mount=mount=.
我认为编写上述代码有两个原因:
exp-01
文本
分析一下
前言分析了代码的执行流程,很明显没有按照预期的顺序执行。让我们先回顾一下预期订单
//step 1created() { //step 1.1让endTimeconst startTime=date . now();console . log(` start created d : $ { start time } ms `);//步骤1.2 this . list=wait this . getlist();end time=date . now();console . log(this . list);console . log(` end created d : $ { end time } ms,cost : $ { end time-start time } ms `);},//step 2 mount ed(){ let end time;const startTime=date . now();console . log(` start mount ed : $ { start time } ms `);console . log(this . list . rows);end time=date . now();console . log(` end mounted : $ { end time } ms,cost : $ { end time-start time } ms `);}//第1步=第1.1步=第1.2步=第2步想要的打印结果是:
//步骤1(创建)开始创建//this . list { _ _ ob _ _ : Observer }结束创建创建成本: 3171.545166015625ms//步骤2(装入)开始装入//This。名单。row【{…},_ _ ob _ _ : observer】end mounted cost : 2.88623046875 ms对比实际打印和预期打印,我们知道问题出在创建的钩子中使用的await的异步代码,没有达到“异步代码同步执行”的预期效果,只确定
我们来分析一下为什么会出现这种意想不到的结果!
在分析之前,我们先复习一些javascript的基础知识!请看下面的代码:
(function _ _ main(){ console . log(' start ');setTimeout(()={ console . log(' setTimeout中的控制台');}, 0);console . log(' end ');})()//setTimeout中的outputstartendconsole
这个印刷订单让你想起什么了吗?
任务队列!
我们都知道JavaScript代码可以分为两类:
同步代码和异步代码
同步代码将按照写入顺序在主线程中执行;
异步代码的触发过程(注意是被触发的,比如异步请求的发起是由主线程同步触发的)是同步的,但异步代码的实际处理逻辑(回调函数)会在异步代码响应时将处理逻辑代码推入任务队列(也叫事件队列)。浏览器会在主线程(当前执行环境的同步代码)执行完代码后的某个时间段检查任务队列。如果有任何任务需要处理,它会让任务排在队列的最前面
例如,现在我们启动一个异步请求:
//exp-02 console . log(' start ');axios . get(' http://XXX.com/GetList ')。然后((resp)={ console . log(' handle response ');}) .catch((错误)={ console.error(错误);});console . log(' end ');在主线程中,以下过程可能首先发生:
//exp-03//step 1 console . log(' start ');//step 2 xios . get(' http://XXX.com/GetList ');//此时回调函数(即当时里面的逻辑)还没有被调用。//step 3 con sole . log(' end ');看看浏览器此时在做什么!
这个时候,Event Loop登场了,但是这个时候没有登场,而是一直在!
“事件轮询”机制会在某个时间段检测任务队列中是否有可执行的任务(所谓的任务其实就是回调),并会立即执行。
当步骤2的请求响应时,异步请求的回调函数将被添加到任务队列或事件队列中,然后当下一个事件轮询检测到任务队列时,队列中的任务将依次出列并进入主线程执行:即执行以下代码:
//如果没有错误((resp)={ console . log(' handleresponse ');})()在这里,我简单的学习了任务队列的机制,关联了exp-01代码,大概知道了意外结果的原因!
创建的钩子中的await函数在一定程度上是同步的,但是它仍然被挂起。实际的处理逻辑(this.list=resp.xxx)在响应完成后被添加到任务队列中,并在主线程的同步代码被执行后被执行。以下是将延迟时间设置为0后的打印:
start created start mount dun defined mount cost : 2.88623046875 ms { _ _ ob _ _ : observer } end created cost 3360 9.76611328125 ms这一面显示了await函数确实被挂起了,回调被添加到任务队列中,等待在主线程代码执行完之后被执行。
那为什么exp-01的代码会同步到一定程度呢?
同步执行的另一个含义是阻止当前线程的继续执行,直到当前逻辑被执行~
看exp-01打印:
{ _ _ ob _ _ : Observer } end created created cost : 3171.5166015625 ms end created是主线程的代码。如果是一般的异步请求,这个打印应该是yo之前的打印{_ _ ob _ _ : observer},但是这里没有很多。
另外,这里有一集。您应该注意到,我一直强调回调函数是在响应完成后添加到任务队列中的。是的,它是!
但是在你弄清楚这个机制之前,你可能有两个猜想:
1.当异步代码被触发时,处理逻辑将被添加到任务队列中;2.如上所述,处理逻辑将在异步代码响应完成后添加到任务队列中;
事实上,你可以推断出来
队列的数据结构以先进先出为特征
此时,假设主线程中有两个异步请求,如下所示:
//exp-04 synccrequest 01(callback 01);syncrequest 02(callback 02);假设处理机制如第一点所述,callback01将首先添加到任务队列,然后callback02。
然后,我们假设syncRequest01的响应时间为10s,syncRequest02的响应时间为5s。
在这里,你是否意识到不服从的感觉!
异步请求的实际性能如何?谁的回调先执行,对吗?实际性能是callback02会在callback01之前执行!
然后,基于这个事实,看看上面的假设(callback01将执行)~
好的。插曲结束!
解决办法
首先,让我回顾一下目的。路由组件严重依赖异步请求返回的数据,因此我们希望阻止组件的呈现过程,并等待异步请求响应。
这是我们需要做的,需要强调的是,我们对数据的依赖性很强,言下之意就是如果数据没有按预期返回,会导致后续逻辑不可避免的异常。
接下来,我们需要讨论解决方案!
检查组件中的布线保护!
before route terbefore route date(在2.2中添加)beforeRouteLeave
这里需要使用的路由保护是:在路由输入之前,先看看代码:
//exp-05 export default { BeforeRouter(to,from,next){ this . showLoading();this.getList()。然后((resp)={ this . HideLoadIng();this . list=resp . data;next();}) .catch((错误)={ this . HideLoadIng();//句柄错误});},mounted(){ let end time;const startTime=date . now();console . log(` start mount ed : $ { start time } ms `);console . log(this . list . rows);end time=date . now();console . log(` end mounted : $ { end time } ms,cost : $ { end time-start time } ms `);},};路由中心之前的路由防护。当此钩子被触发时,主线程将被阻塞,页面将保持挂起,直到调用BeforeRouterCenter的下一个回调函数,路由将被跳转以呈现新的路由组件。
看来这个解决方案还是挺适合以上要求的,可以先拉数据再调用下一个!
然而,正如刚才提到的,页面一直在装死。如果加入数据采集的时间太长,难免会变丑,用户体验太差
所以在exp-05中,我分别在请完之前和之后调用了this.showLoading()和this.hideLoading(),这样页面就可以保活了。
这个处理假死的加载会提醒你写什么吗?是的,下面的github跳转页面是顶部的一个蓝色小条
想想有点酷。当然,改善用户体验的方法有很多,比如全屏加载为body的子元素,或者按钮加载等.
当然,我们知道如何屏蔽主线程,而加载只是一种自欺欺人的优化(这个成语此时不是贬义词)!
因此,与其严重依赖数据,不如在路线的挂钩处抓取数据,让用户“更快”跳转到目的页面。为了避免页面对数据的依赖引发异常(可能是xxx的未定义),我们可以预设一些初始数据,比如exp-01中对this.list.rows的依赖,我们可以预设this.list:
list : { rows 3360[]}这样就不会抛出异常,而当异步请求完成后,基于vue的更新机制会再次呈现我们期望的数据~
摘要
至于exp-01的写法,不能说是错的,也不能说是不好的。一切都取决于我们的目的。如果只是为了保证多个异步函数的执行顺序,写exp-01没有错,所以async/await不能用在路由钩子什么的上面!
版权声明:详细解释vue中异步等待的误解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。