Web Development 網站開發 (10) - Javascript 非同步:Promise
Promise 是 Javascript 用來處理非同步事件的方法。如果只用 Callback 來處理非同步,當程式變得複雜並且有多層非同步事件需要處理時,程式架構會變的不好維護。Pormise 被介紹出來之後,很好的提升了非同步事件的管理與執行。除此之外也增加了程式碼的可讀性。
Callback Hell
當有很多 Callback 相互呼叫時,程式碼一層一層寫下去變得可讀性很低:
最容易的解決方法就是不要用匿名的 Callback,把每個 Callback Function 都定義名稱,然後從外層引用。
// 把所有 Callback 打平然後從外層呼叫,
// 當然這樣做的缺點就是你會常宣告只被用一次的 function,而且這樣的定義通常沒什麼意義
function doA() {
/* do A */
setTimeout(doB, 200);
}
function doB() {
/* do B */
setTimeout(doC, 300);
}
function doC() {
/* do C */
setTimeout(doD, 400);
}
function doD() {
/* do D */
}
setTimeout(doA, 100);
Promise
Promise 在 ES6 (2015 年) 才被介紹出來,就如上所說,主要的目的就是增強 Javascript 對非同步事件的處理。 先看看以下例子,如何把上面的 setTimeout 任務改成 Promise 處理:
執行一個 Promise 跟我們會用 .then
,Promise 在被呼叫時一開始會是 Pending 狀態,意思其實就是在 Event Loop 裡準備要執行的意思,一但 Call Stack 空了這個任務就會被加進去。then
裡面我們需要定義一個 Function,這個 Function 的功用其實就是 Callback,它會在非同步任務執行完後被呼叫。
我們可以自己定義 Promise,基本上可以把所有各種本來是用 Callback 的非同步事件都包成 Promise。
// 要宣告一個 Promise 如以下:
new Promise(function(resolve, reject) {
// do something.
// 如果有結果要回傳到外面的話,我們會呼叫 resolve 並帶上要回傳的結果。
// 如果有出錯要回傳錯誤訊息則是用 reject
});
// 在回頭看看上面用的 setTimeoutPromise, 帶入 setTimeout 所需的間隔時間 (ms)
// 回傳一個自定義的 Promise
// 我們在 setTimeout 裡呼叫了 resolve,代表了這個 promise 的完成。因為沒有要回傳東西,所以直接用了 resolve()。
const setTimeoutPromise = function(ms){
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, ms);
});
};
Promise Chaining
Promise 沒有類似 Callback Hell 的原因是因為他有著 Promise Chaining 的特性。Promise 的 then 可以回傳另一個 Promise,也代表了這個 then 後面還可以接另外一個 then 去處理前一個 then 回傳的 Promise。
// 因爲我們的 then 裡面 return 了新的 Promise (setTimeoutPromise(xx))
// 所以 return 出新的 Promise 可以用下一個 then 去處理。
// .then().then().then() ... 形成了 Promise Chaining
setTimeoutPromise(100).then(function() {
// do A...
return setTimeoutPromise(200)
}).then(function() {
// do B...
return setTimeoutPromise(300)
}).then(function() {
// do C...
return setTimeoutPromise(400)
}).then(function() {
// do D...
});
Promise 裡面還有很多功能,比如說 .catch
來捕捉 Promise 拋出的錯誤、Promise.all 去同時執行多個 Promise 然後匯聚結果等。更多細節可以參考
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise
有了 Promise 以後我們以為世界變得美好,但其實不是。定義一堆 Promise 然後 then 來 then 去,一樣讓我們的程式變得很複雜。一行一行執行的程式看起來才是最直覺易懂的。
所以新的 Promise 寫法又誕生了:Async and Await... 。