🥜 前情提要
上回我們講了網頁中的下載是如何實施,而其中有個關鍵字異步,我們這邊就進行解釋同步和異步是什麼
同步和異步:
用最簡單和粗糙的說法:
假設今天你要打電話問書局老闆說「人類簡史」賣光了沒:

同步:老闆會說等一下我去找找,你就拿著電話筒等了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()
|