1. 程式人生 > >async/await函式的執行順序的理解

async/await函式的執行順序的理解

最近遇到一個關於async函式使用的Bug,因程式碼涉及太多業務,所以模擬了程式碼, 如下:

let testArr = [1, 2, 3]
let flag = false

const func = (res) => {
  return new Promise((resolve, reject) => {
    if (res) {
      resolve(res)
    }
  })
}

testArr.forEach(async (item) => {
  await func(item).then(res => {
    flag =
true console.log('res', res, flag) }) }) console.log('flag', flag)

當時寫程式碼的人的目的很簡單,就是要讓非同步函式變成同步來執行,按如下輸出:

res 1 true
res 2 true
res 3 true
flag true

但實際輸出的是:

flag false
res 1 true
res 2 true
res 3 true

當時我也覺得奇怪的,為什麼await沒有生效?真的沒有生效?

於是我在await 後面加了一個console.log(‘inside’, flag), 程式碼如下

let testArr = [1, 2, 3]
let flag = false

const func = (res) => {
  return new Promise((resolve, reject) => {
    if (res) {
      resolve(res)
    }
  })
}

testArr.map(async (item) => {
  await func(item).then(res => {
    flag = true
    console.log('res', res, flag)
  })
  console.
log('inside', flag) }) console.log('flag', flag)

輸出如下

flag false
res 1 true
res 2 true
res 3 true
inside true
inside true
inside true

也就是說,其實在函式裡面await是生效了?那為是什麼外面就沒有生效?

很多人以為await會一直等待之後的表示式執行完之後才會繼續執行後面的程式碼,實際上await是一個讓出執行緒的標誌

await後面的函式會先執行一遍,然後就會跳出整個async函式來執行後面js棧的程式碼。

等本輪事件迴圈執行完了之後又會跳回到async函式中等待await後面表示式的返回值。

如果返回值為非promise,則繼續執行async函式後面的程式碼,

否則將返回的promise,放入promise佇列(Promise的Job Queue), 然後等待promise任務佇列執行完之後,再執行await後面的程式碼

所以,如果要讓flag變成true,需要再用一個async函式,修改的程式碼如下:

let testArr = [1, 2, 3]
let flag = false

const func = (res) => {
  return new Promise((resolve, reject) => {
    if (res) {
      resolve(res)
    }
  })
}
async function container () {
  await testArr.map(async (item) => {
    await func(item).then(res => {
      flag = true
      console.log('res', res, flag)
    })
    console.log('inside', flag)
  })
  console.log('flag', flag)
}
container()

輸出:

res 1 true
res 2 true
res 3 true
flag true
inside true
inside true
inside true

從其他博主看到這樣一段程式碼,我覺得非常經典:

function testSometing() {
  console.log("執行testSometing");
  return "testSometing";
}

async function testAsync() {
  console.log("執行testAsync");
  return Promise.resolve("hello async");
}

async function test() {
  console.log("test start...");
  const v1 = await testSometing();//關鍵點1
  console.log(v1);
  const v2 = await testAsync();
  console.log(v2);

  console.log(v1, v2);
}

test();

var promise = new Promise((resolve)=> { console.log("promise start.."); resolve("promise");});//關鍵點2
promise.then((val)=> console.log(val));

console.log("test end...")

輸出:

test start...
執行testSometing
promise start..
test end...
testSometing
執行testAsync
promise //第七位
hello async
testSometing hello async

調整了一下程式碼順序

function testSometing() {
  console.log("執行testSometing");
  return "testSometing";
}

async function testAsync() {
  console.log("執行testAsync");
  return Promise.resolve("hello async");
}

async function test() {
  console.log("test start...");
  const v2 = await testAsync();
  console.log(v2);
  const v1 = await testSometing();//關鍵點1
  console.log(v1);

  console.log(v1, v2);
}

test();

var promise = new Promise((resolve)=> { console.log("promise start.."); resolve("promise");});//關鍵點2
promise.then((val)=> console.log(val));

console.log("test end...")

輸出:

test start...
執行testAsync
promise start..
test end...
promise    //第五位
hello async
執行testSometing
testSometing
testSometing hello async

區別主要是’promise’的出現的位置,所以:

如果返回值為非promise,則繼續執行async函式後面的程式碼,哪怕外面已經有任務佇列在排隊

否則將返回的promise,放入promise佇列(Promise的Job Queue), 然後等待promise任務佇列執行完之後,再執行await後面的程式碼