iT邦幫忙

2019 iT 邦幫忙鐵人賽

0

要處理同步/非同步的執行,在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();

https://ithelp.ithome.com.tw/upload/images/20181115/20112573SjavWa5qIn.png

首先看到,在函式前面加上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);

https://ithelp.ithome.com.tw/upload/images/20181115/20112573nqIYLP5kaY.png

各位有沒有覺得這種方式似乎跟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();

https://ithelp.ithome.com.tw/upload/images/20181115/20112573VZ9bz8D1E7.png
不管執行多少次,也不管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);
    }
);

https://ithelp.ithome.com.tw/upload/images/20181115/20112573H55VBdZGhV.png

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);
    }
);

https://ithelp.ithome.com.tw/upload/images/20181115/20112573uYvpmu3Hat.png

必須將所有的非同步執行放入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();

有可能出現:
https://ithelp.ithome.com.tw/upload/images/20181115/20112573QWYARNkZpa.png
不要忘記async函式的執行環境是call stack,它是以同步的方式執行,跟函式內部的非同步的執行環境不同,每個async都有自己的執行環境,彼此之間不受影響。


上一篇
window.setTimeout
系列文
JavaScript Note31

尚未有邦友留言

立即登入留言