🥜 前情提要

上回我們講了網頁中的下載是如何實施,而其中有個關鍵字異步,我們這邊就進行解釋同步和異步是什麼

同步和異步:

用最簡單和粗糙的說法:
假設今天你要打電話問書局老闆說「人類簡史」賣光了沒:

  • 同步:老闆會說等一下我去找找,你就拿著電話筒等了1小時,之後老闆說賣光了。

  • 異步:老闆會說等一下我去找找,你把A和B的電話給我,

    • 有結果的話我再打給你A號碼的手機
    • 沒結果的話我再打給你B號碼的手機

    之後你把電話掛掉,一小時候老闆打電話給你A電話,你就知道結果了

以上就是同步和異步的概念,這兩種概念在程式語言佔據很重要的一部分,想想如果我是要下載1GB的檔案,沒有異步的話,你就不能邊看影片邊下載,那麼異部我們用JavaScript來當作範例,async是撰寫異步的寫法,他是基於Promise的功能,所以在撰寫異步範例之前,我們需要知道那麼Promise是什麼?

Promise:

今天要談異步操作,那我們看上述書局異步Case,Promise 就是

  • 你把A電話給老闆,預期有結果會通知A電話
  • 你把B電話給老闆,預期沒結果會通知B電話

上述這兩種情境,在JavaScript中可以用對應的函數表達,所以使用Promise時候可以傳入兩個函數,這兩個函數分別代表有結果就執行第一個,沒結果就執行第二個,當然也可以只傳一個函數,以下我們用傳兩個函數的當作範例。

所以以下來看範例。

Promise範例 循序執行範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function sucess (ans) {
console.log("這是A,Sucess:" + ans)
}

function fail (ans) {
console.log("這是B,Fail:" + ans)
}

function findBookProcess (resolve, reject) {
// resolve 就是有結果函數。 reject 就是沒結果函數
setTimeout(() => { // 使用延遲N秒,模擬老闆找書
const book = null;
if(book != null) {
resolve("找到了");
} else {
reject("找不到書");
}
}, 10000); // 我們假設老闆花了10秒找書
}

const promise1 = new Promise(findBookProcess);
promise1.then(sucess , fail);

console.log("開始找書");

在上述的案例,有找到通知Sucess,沒結果通知Fail,那麼我可不可只有Sucess才通知,Fail就不通知?這也是可以的

1
2
const promise1 = new Promise(findBookProcess);
promise1.then(sucess);

而當我收到通知後我就必須去老闆的店,所以還可以這樣寫

1
2
3
4
5
6
7
promise1
.then(Sucess)
.then(拿錢包)
.then(搭捷運)
.then(到老闆的店)
.then(按電鈴)
.then ...etc

這就是循序執行的Promise運用

Promise範例 併發執行範例:

在上述的Case,你問老闆 -> 老闆找 -> 老闆的回應,這是一個循序程序,那麼如果今天情況是:

你問老闆 -> 老闆問同行A、B、C -> 老闆自己沒有 但是同行A、B、C還沒有結果 -> 老闆等同行A、B、C回應 -> 都有回應,通知你結果。

很明顯情況變複雜了,因為此時有4個人幫你找,情況就變成以下

  • 其中一人有書,但你還是要等所有人回應,因為你想知道最近的書局在哪
  • 其中一人有書,我馬上過去我急需這本書。

而Promise有提供便利的方法可以實現上述兩種情況。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function sucess (allAns) { 
for(let res of allAns)
console.log(res)
};

function sucess_race (ans) {
console.log("最快回應的老闆:" + ans)
}

const promise老闆 = new Promise(.... 請參考上個範例這邊不重新打了);
const promise朋友A = new Promise(.... 請參考上個範例這邊不重新打了);
const promise朋友B = new Promise(.... 請參考上個範例這邊不重新打了);
const promise朋友C = new Promise(.... 請參考上個範例這邊不重新打了);

Promise
.all([promise老闆, promise朋友A, promise朋友B, promise朋友C])
.then(allSuccess); // 所有老闆的回應都結束,就會呼叫success函數

Promise
.race([promise老闆, promise朋友A, promise朋友B, promise朋友C])
.then(sucess_race); // 四個當中誰最先回應

還有很多用途,這邊不舉例,詳情請看:網站

Promise範例 錯誤處理範例:

Promise可以併發執行 加上 循序執行 像是:

1
2
3
4
5
6
7
Promise
.all([promise老闆, promise朋友A, promise朋友B, promise朋友C])
.then(sucess)
.then(拿錢包)
.then(搭捷運)
.then(到老闆的店)
.then(按電鈴);

很明顯會執行到很多函數,複雜度提高很多,所以我們可以加上Catch進行捕捉錯誤。

1
2
3
4
5
6
7
8
Promise
.all([promise老闆, promise朋友A, promise朋友B, promise朋友C])
.then(sucess)
.then(拿錢包)
.then(搭捷運)
.then(到老闆的店)
.then(按電鈴)
.catch(發生意外叫我弟幫我拿)

Await/Async:

上述的程式碼我們知道如何撰寫異步的JS程式碼,而如果我們需要同步的效果的話可以使用在ES7加入Await/Async

根據規則,如果要用同步必須使用await語句,而await必須存在async宣告的函數中,所以大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function findBook(bookName) {
return new Promise((resolve , reject)=>{
setTimeout(() => { // 使用延遲N秒,模擬老闆找書
const book = "人類簡史";
if(book == bookName) {
resolve("找到了");
} else {
reject("找不到");
}
}, 10000); // 我們假設老闆花了10秒找書
});
}

async function main () {
const res = await findBook("人類簡史");
console.log(res)
// await 拿錢包()
// await 去捷運()
// await 敲門()
// const book = await 拿書()
};

main()