async/await函式的執行順序的理解
阿新 • • 發佈:2018-12-05
最近遇到一個關於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後面的程式碼