关于异步执行顺序的理解

一前言


相信很多人都被异步执行,搞得晕晕的,异步任务不按常理出牌,难以理解。我以自己的理解尝试给大家解释一下。说的不对,多多指教。


 二 基础知识


首先js执行过程
所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

主线程之外,还存在"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

微任务先于宏任务执行

主线程不断重复上面的第三步
我们知道异步任务分为微任务,和宏任务。不知道也没关系,
宏任务: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务: process.nextTick(Nodejs), Promises, Object.observe, MutationObserver;
理论就是如此了
(注意:如果你不知道队列的特性,很可能就搞不懂接下来的发生的问题)
话不多说,直接来题

 

 

setTimeout(function () {
  console.log('8')
}, 0) 
async function async1() {
  console.log('1')
  const data = await async2()
  console.log('6') 
  return data
}
async function async2() {
   return new Promise(resolve => {
    console.log('2')
    resolve('async2的结果')
  }).then(data => {
    console.log('4') 
    return data
  })
}
async1().then(data => {
  console.log('7')
  console.log(data)
})
new Promise(function (resolve) {
  console.log('3')
  resolve()
}).then(function () {
  console.log('5') 
})


是不是直接懵了!

 

别急 我们一步一步分析
首先setineout是宏任务,加入宏任务队列向下执行定义,又是定义不管
然后执行到async1(),
console.log('1')同步代码直接打印
执行async2
promise内的代码立即执行。console.log('2')此时将.then后面代码console.log('4')加入微任务队列
然后返回async1
知识点;await后面的代码可以看成一个微任务队列,并且会阻塞代码
此时跳出了async1
到了new promise。
promise内的代码立即执行console.log('3')
然后将.then()后面的 console.log('5') 加入微任务队列,
此时微任务中有console.log('4')console.log('5')根据队列先进先出的顺序输出
然后再跳回到await async2()将后面的 console.log('6') 加入微任务
此时跳到async1().then(data => {
  console.log('7')
  console.log(data)
})将.then后面的  console.log('7')console.log(data)加入微任务。此时又执行完了,输出微任务队列的任务。最后执行宏任务的定时器任务
所以执行顺序是
**1 2 3 4 5 6 7 async的结果 8**
学废了吗

再来一道试试

async function async1() {
  console.log('async1 start') 
  await async2()
  console.log('async1 end')1
}
function async2() { // 去掉了 async 关键字
  console.log('async2');
}
console.log('script start') 
setTimeout(function () {
  console.log('setTimeout') 
}, 0)
async1();
new Promise(function (resolve) {
  console.log('promise1')
  resolve();
}).then(function () {
  console.log('promise2')
})
console.log('script end')


首先两个函数定义不管
同步代码console.log('script start') 直接输出
定时器宏任务放入宏任务队列
执行async1
直接输出console.log('async1 start') 

执行async2直接输出console.log('async2');
await后面console.log('async1 end')加入微任务队列。

new promise体内立即执行console.log('promise1')
.then后面的  console.log('promise2')加入微任务队列
然后直接输出console.log('script end')。

现在要执行微任务队列的
顺序输出console.log('async1 end')console.log('promise2')
微任务执行完毕
执行宏任务,将 console.log('setTimeout') 输出,执行完毕
所以顺序是
script start
 async1 start
async2
 promise1
 script end
 async1 end
 promise2
 setTimeout
如果搞不清楚就回上去看理论知识。所有的顺序都基于理论,然后多做几道题,在温故知新。
注意:不同浏览器,或者node环境里顺序会造成细微差别实属正常
希望这篇文章对或多或少你有帮助

做个测试吧

前端工程师 面试题


最后 瑞斯掰~