要處理同步/非同步的執行,在ES5以前是Callback Hell,ES6是Promise,ES7是async/await,語法更簡潔扼要。
我們先執行範例,也是延續上個章節的,各位可以比較Promise與async/await的不同。
function async (value) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (value)
resolve(value);
else
reject('無輸入值');
}, Math.floor(Math.random() * 2000));
})
}
async function asyncProcess() {
let taskA = await async ('任務A');
let taskB = await async ('任務B');
let taskC = await async ('任務C');
console.log(taskA, taskB, taskC);
}
asyncProcess();
首先看到,在函式前面加上async,表示函式內部有非同步的操作。
函式內部,在每個會回傳Promise的非同步函式前加上await,表示該函式在回傳Promise之前,後面的程式碼都不會執行。
A執行完再換B執行,再來是C執行,前面沒有執行完成,後面不會執行,是因為有await這個關鍵字,幫我們擋住。
async/await算是共同體,await只能在async函式裡執行,如果單獨在全域環境執行,會發生錯誤。
let taskA = await async ('任務A');
let taskB = await async ('任務B');
let taskC = await async ('任務C');
console.log(taskA, taskB, taskC);
各位有沒有覺得這種方式似乎跟Promise.all很像,全部的非同步函式執行完成後,再進行下一個階段,那他們的差異是甚麼呢?
Promise.all([
async ('任務A'), async ('任務B'), async ('任務C')
]).then(
response => {
console.log(response);
}
).catch(
error => {
console.log(error);
}
);
Promise.all函式一樣是依序執行所有的非同步,但它並不會等到一個非同步完成,再執行下一個,換個角度來說,它幾乎是同時間執行所有非同步,誰先執行完成,對它來說沒有差,因為它會等到所有的執行完成,再決定接續執行then( )/catch( )。
稍微修改async/await的範例,各位可以看得更清楚:
async function asyncProcess() {
let taskA = await async ('任務A');
console.log(taskA);
let taskB = await async ('任務B');
console.log(taskB);
let taskC = await async ('任務C');
console.log(taskC);
}
asyncProcess();
不管執行多少次,也不管ABC任務執行的時間有多少,它一定會讓這些任務依序執行,也可以這麼說,它是以同步的方式執行非同步。
比起之前Promise跟then的搭配,相信這種方式的可讀性更高。
雖然ES7推出的async/await,可以讓我們使用類似同步的語法來撰寫,但它骨子裡還是Promise,並不是說async/await的出現,可以讓我們無須了解Promise的觀念,這是錯的。
async/await跟Promise是可以一起使用的:
async function asyncProcess() {
let task = await Promise.race([async ('任務A'), async ('任務B'), async ('任務C')]);
console.log(task);
}
asyncProcess();
async函式會回傳Promise,這也意味著,可以根據Promise的狀態實現(fulfilled)/拒絕(rejected)來執行then( )/catch( )。
async function asyncProcess() {
let taskA = await async ('任務A');
let taskB = await async ('任務B');
let taskC = await async ('任務C');
return `${taskA},${taskB},${taskC}`;
}
asyncProcess().then(
response => {
console.log(response);
}
).catch(
error => {
console.log(error);
}
);
async function asyncProcess() {
let taskA = await async ('任務A');
let taskB = await async ('');
let taskC = await async ('任務C');
return `${taskA},${taskB},${taskC}`;
}
asyncProcess().then(
response => {
console.log(response);
}
).catch(
error => {
console.log(error);
}
);
必須將所有的非同步執行放入async函式,才能確保依序執行,但如果是以下這種方式,是沒有意義的:
async function asyncProcess() {
let taskA = await async ('任務A');
console.log(taskA);
let taskB = await async ('任務B');
console.log(taskB);
}
async function asyncProcess1() {
let taskC = await async ('任務C');
console.log(taskC);
let taskD = await async ('任務D');
console.log(taskD);
}
asyncProcess();
asyncProcess1();
有可能出現:
不要忘記async函式的執行環境是call stack,它是以同步的方式執行,跟函式內部的非同步的執行環境不同,每個async都有自己的執行環境,彼此之間不受影響。