最近在开发过程中,一个外包同学遇到这样一段代码,他不太能理解为什么执行顺序不是他期望的,让我帮他解答一下,后来我看了一下,还蛮有意思的,我还给域内同学看了一下,有的也踩到坑里去了,于是我写了一段简版的,分享给大家
 
 
添加图片注释,不超过 140 字(可选)
这段代码执行完,应该会打印什么呢?
那位同学期望的是:
// catch error // async catch
可结果并非如此,打印结果如下:
// async catch // catch error
这是为什么呢?
于是,有人提出猜想,是因为Promise.reject优先执行了,还没进showloading方法就直接报错了,因此优先被try catch捕获了,所以先打印了async catch
于是我们加了一点console
 
 
添加图片注释,不超过 140 字(可选)
执行结果如下:
// start // end // async catch // catch error
事实并不是这位同学猜想的,而是代码确实执行到了showloading内部,执行完成后,先被try catch捕获了。
那为什么会这样呢?
原因是showloading里面有个坑,这里看似是被await阻塞了,其实并没有,因为showloading只是一个return promise的普通函数而已,所以其实内部依然是按同步执行的,res.catch作为一个微任务,执行到的时候被挂起了,放在了微任务的队列中等待执行,而try catch是主线程上的,所以优先执行,待主线程上任务执行完成后,再去执行微任务队列。(更多细节,参考Event Loop)
 
那问题来了,如何能让打印结果达到预期的顺序呢?
很简单,只要让await阻塞住res.catch就行了
所以第一个方法很简单,在showloading内部添加async await,异步转同步
 
 
添加图片注释,不超过 140 字(可选)
这样,打印结果就变成了
// start // catch error // end // async catch
OK,可以了!
如果不改变showloading内部逻辑,还有别的方法么?
有,如果阻塞不加在showloading内部,我们就加在它的外部
 
 
添加图片注释,不超过 140 字(可选)
这样,打印结果也能满足我们的预期
// start // end // catch error // async catch
有同学就问了,为什么加了一个then就可以了呢,这种原理是什么呢?
其实还是执行顺序的问题,宏任务有执行顺序,微任务也是:
刚刚说到res.catch作为微任务被挂起了,排在了微任务队列中,而这时候showloading().then()也是一个微任务,因此也被挂起了,只是由于执行顺序,它被放在了前一个微任务的后面,await会等待showloading().then()的执行完成,因此async catch会在最后打印。
这时候,我们再改一下代码
 
 
添加图片注释,不超过 140 字(可选)
那么此时会打印什么呢?根据上面第一个场景,有的同学会猜想答案应该是这样:
// start // end // await then // then function
但其实并不是这样,答案应该是这样:
// start // end // then function // await then
这里有同学会很意外,刚刚不是说不加showloading()后面不加then不会阻塞嘛,为什么await then会在之后打印呢?
原因是因为以上代码其实等同于
 
 
添加图片注释,不超过 140 字(可选)
所以解释原理同后面加个then了。
以上就是今天分享的内容了,感兴趣的同学可以留言一起讨论哈!
文字免费,但码字不易,记得点赞!
感兴趣可关注一波,谢谢!
posted on 2025-05-29 14:56  言先生  阅读(6)  评论(0)    收藏  举报