async與await輸出順序的思考


async

  1. async其實就是對Generator的封裝,只不過async可以自動執行next()。

  2. async必須等到里面所有的await執行完,async才開始return,返回的Promise狀態才改變。除非遇到return和錯誤。

  3. async默認返回一個Promise,如果return不是一個Promise對象,就會被轉為立即resolve的Promise,可以在then函數中獲取返回值。

例一

async function async1() {
    console.log("async1_start_2");
    await async2();
    console.log("async1_end_6");
    return 'async_return_8';
}
 
async function async2() {
    console.log("async2_3");
}
 
console.log("script_start_1");
 
setTimeout(function() {
    console.log("setTimeout_9");
}, 0);
 
async1().then(function (message) { console.log(message) });
 
new Promise(function(resolve) {
    console.log("promise_4");
    resolve();
}).then(function() {
    console.log("promise_7");
});
 
console.log("script_end_5");

輸出為:

image-20200830003450311

這道題目考查的是我們對 事件循環 任務隊列 的理解:

事件循環(Event Loop):

  1. JS會首先判斷代碼是同步還是異步,同步進入主線程,異步進入任務隊列;
  2. 同步任務進入主線程后一直執行,直到主線程空閑后,才會去任務隊列中查看是否有可執行的異步任務,如果有就推入主線程中執行;
  3. 事件循環是一個先進先出(FIFO)隊列,這說明回調是按照它們被加入隊列的順序執行的。

[ 分析 ]:

  1. 在單線程的js中,異步代碼會被放入一個事件隊列,等到所有其他代碼執行后再執行,而不會阻塞線程。我們從上到下看,首先打印:1

  2. setTimeout / setInterval 要放到任務隊列的末尾,等待后續執行。繼續往下走;

    此時的任務隊列:

  • 宏任務隊列: setTimeout
  • 微任務隊列:none
  1. async1 開始執行,當函數里遇到await時,暫停執行(await所在行放在本次執行完),而 async1 函數 未完成部分被添加到宏任務隊列;

    此時的任務隊列:

  • 宏任務隊列:async1, setTimeout
  • 微任務隊列:none
  1. new Promise() 實例對象被new出來后,它里面的promise1會立刻打印,然后又遇到 then, 此時 promise 實例 被添加到微任務隊列;

    此時的任務隊列:

  • 宏任務隊列:async1 ,setTimeout
  • 微任務隊列: promise實例
  1. 接着打印:script end。至此,同步代碼(第一個宏任務)已執行完畢。而我們的任務隊列中還存在着 async1, promise對象, setTimeout異步回調;

  2. 由於異步代碼第一次執行時,async1 函數 要早於 promise對象,所以緊接着 async1 函數繼續執行沒有執行完成的部分(例三中promise.then先於await,所以then先執行),執行完畢后,退出任務隊列,打印:async1 end。然后把它的 then 邏輯添加到任務微任務隊列中;

此時的任務隊列:

  • 宏任務隊列:setTimeout

  • 微任務隊列:promise實例 ,async1的then邏輯部分

  1. 先清空微任務隊列,promise 實例 繼續執行它的 then 的邏輯,打印:promise2。執行完畢后,退出微任務隊列;

此時的任務隊列:

  • 宏任務隊列:setTimeout

  • 微任務隊列:async1的then邏輯部分

  1. async 函數執行 then 邏輯;

此時的任務隊列:

  • 宏任務隊列:setTimeout

  • 微任務隊列:none

  1. setTimeout是宏任務會在最后執行。

例二

console.log(1);
async function asyncfn1(){
    console.log(2);
    await asyncfn2();
    console.log(5);
};
setTimeout(() => {
    console.log('setTimeout')
}, 0)
 
async function asyncfn2(){
    console.log(3)
};
 
asyncfn1();
console.log(4);

輸出:

1
2
3
4
5
setTimeout

細品:

  1. 首先執行全局同步代碼,先輸出 1
  2. 隨后執行asyncfn1,輸出 2 遇到awati后,先執行asyncfn2,將后面的代碼放入宏任務隊列,

此時的任務隊列:

  • 宏任務隊列:asyncfn1剩余代碼;
  • 微任務隊列:none;
  1. 執行asyncfn2輸出 3
  2. 繼續執行全局同步代碼,輸出 4
  3. 執行宏任務輸出 5

例三

var p = new Promise((res,rej) => {
  res('hello_6')
  console.log('1')
})

function hello() {
  console.log('hello_begins_2')
  return p
}

hello().then(res => {
  console.log(res)
  console.log('hello_7')
  return 'hello_10'
}).then(res => {
  console.log(res)
  console.log('hello_11')
  return 'hello_13'
}).then(res => {
  console.log(res)
  console.log('hello_14')
})

function test1 () {
  console.log('test1_5')
}

async function asy () {
  console.log('asy_begins_3')
  await console.log('asy_4')

  console.log('async_8')
  await console.log('asy_9')

  console.log('asy_ends_12')
}

asy()

test1()

結果:
image-20200829235047575

看官們可以根據輸出結果細品;

注意:await后面的代碼雖然算作宏任務,但是和普通的微任務不在一個維度,位於更上一層的任務隊列,所以優先度要比其他(下層)微任務要高;

參考思路:

  1. 執行同步的全局代碼輸出 1(遇到new Promise()的需要立即執行)
  2. 11 行執行 hello函數,輸出2,並返回一個Promise對象p,將hello函數的第一層then函數放入微任務隊列;

此時的任務隊列:

  • 宏任務隊列:none

  • 微任務隊列:hello.then(0)

  1. 繼續執行同步代碼,到第38行,執行asy函數;
  2. 在第29行輸出3,隨后遇到await,執行該行,輸出4,剩下的代碼被放入了宏任務隊列(為了區分任務的層次,標明了序號,先執行完同層的任務,再到其他層)

此時的任務隊列:

  • 宏任務隊列:asy await后代碼(0)

  • 微任務隊列:hello.then(0),hello.then.then(1),hello.then.then.then(2)

  1. 繼續執行同步全局代碼,第40行,執行test函數,輸出5
  2. 執行微任務隊列中的hello.then,輸出返回的promise對象p中的處理結果6(第12->8->2行),隨后第13行輸出7

此時的任務隊列:

  • 宏任務隊列:asy await后代碼(0)

  • 微任務隊列:hello.then.then(1),hello.then.then.then(2)

  1. 執行第0層的宏任務,也就是asy await后的代碼,第32行輸出8,第39行遇到await,執行完該行輸出9后,將后面的代碼推進宏任務隊列;

此時的任務隊列:

  • 宏任務隊列:asy await后代碼(1)

  • 微任務隊列:hello.then.then(1),hello.then.then.then(2)

  1. 15行,執行hello函數的第二個then函數,返回處理結果res,輸出1011

此時的任務隊列:

  • 宏任務隊列:asy await后代碼(1)

  • 微任務隊列:hello.then.then.then(2)

  1. 同層的還有一個宏任務,執行asy await后的代碼,第35行,輸出12

此時的任務隊列:

  • 宏任務隊列:none

  • 微任務隊列:hello.then.then.then(2)

  1. 取出微任務隊列中的最后一個任務,回到第19行輸出hello的第二個then函數的處理結果,分別是1314

至此程序執行完成;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM