Promise
Promise 代表一個未來將要完成、或失敗的非同步操作。
比喻: 去公家機關尋求幫助前先拿號碼牌,拿到的號碼牌就像是 promise,等到紙張上的號碼到了(resolve),在櫃檯獲得的幫助就像是調用了 callback
建立 Promise
1 2 3 4 5 6 7
| const myPromise = new Promise((resolve, reject) => { resolve([1, 2, 3, 4]); });
myPromise.then(arr => { console.log("Promise myPromse resolved with data: ", arr); });
|
首先我們使用 promise 構造函數,構造函數內有單個回調函數,該回調函數具有兩個參數,resolve 和 reject
因此,我們的非同步任務將根據我們自定義的回調函數來決定調用解決(resolve)或拒絕操作(reject),上面的例子直接 resolve
現在我們創建好了 promise 物件,下一步要做的是在 then 函數中定義解決(resolve)後要做什麼事,這樣的就算是完成了一個基本 promise
處理 Error
1 2 3 4 5 6 7 8 9
| const myPromise = new Promise((resolve, reject) => { reject("ERROR"); });
myPromise.then(data => { console.log("Promise myPromse resolved with data: "+ data); }).catch(data => { console.log("Promise myPromse rejected with data: "+ data); });
|
用隨機產生 Error 來更熟悉 Promise 運作
1 2 3 4 5 6 7 8 9 10
| const myPromise = new Promise((resolve, reject) => { let randomNum = Math.random(); randomNum < 0.5 ? resolve(randomNum) : reject(randomNum) });
myPromise.then(result => { console.log("Success: "+ result); }).catch(error => { console.log("Error: "+ error); });
|
加上 setTimeout 模擬非同步任務
1 2 3 4 5 6 7 8 9 10 11 12
| const myPromise = new Promise((resolve, reject) => { setTimeout(() => { let randomNum = Math.random(); randomNum < 0.5 ? resolve(randomNum) : reject(randomNum) }, 2000); });
myPromise.then(result => { console.log("Success: "+ result); }).catch(error => { console.log("Error: "+ error); });
|
Promise Chaining
首先看看沒有用 Promise 的巢狀非同步任務
1 2 3 4 5 6 7 8 9 10 11 12 13
| let counter = 0; setTimeout(() => { counter++; console.log("Counter: "+ counter); setTimeout(() => { counter++; console.log("Counter: "+ counter); setTimeout(() => { counter++; console.log("Counter: "+ counter); }, 3000); }, 2000); }, 1000);
|
再改進上面的例子為 Promise 前,先來看看 Promise Chaining
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const myPromise = new Promise((resolve, reject) => { setTimeout(() => { let randomNum = Math.random(); resolve(randomNum); }, 2000); });
myPromise.then(num => { console.log("num: "+ num);
return new Promise((resolve, reject) => { setTimeout(() => { num++; resolve(num); }, 2000); }); }).then(result => { console.log("result: "+ result); });
|
- 其實也只是在 then 回傳新的 Promise,這樣就能做到連續非同步的操作
而 Promise 不止能回傳 new Promise 還能回傳 value
1 2 3 4 5 6 7 8 9 10 11
| const myPromise = new Promise((resolve, reject) => { resolve(1); });
myPromise.then(data => { return data*2; }).then(data => { return data*2; }).then(data => { console.log(data) });
|
回到巢狀非同步任務的例子
1 2 3 4 5 6 7 8 9 10 11 12 13
| let counter = 0; setTimeout(() => { counter++; console.log("Counter: " + counter); setTimeout(() => { counter++; console.log("Counter: " + counter); setTimeout(() => { counter++; console.log("Counter: " + counter); }, 3000); }, 2000); }, 1000);
|
換成使用 Promise
- 先把要做的事情拉出來
1 2 3 4 5
| let counter = 0; function incCounter() { counter++; console.log("Counter: " + counter); }
|
- 主程式,在運行這個程式時會把 Promise return 出來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| let counter = 0;
function incCounter() { counter++; console.log("Counter: " + counter); }
function runLater(callback, timeInMs) { const p = new Promise((resolve, reject) { setTimeout(() => { const res = callback(); resolve(res); }, timeInMs); }); return p; }
|
- Promise Chaining 的方式串起來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let counter = 0;
function incCounter() { counter++; console.log("Counter: " + counter); };
function runLater(callback, timeInMs) { const p = new Promise((resolve, reject) => { setTimeout(() => { const res = callback(); resolve(res); }, timeInMs); }); return p; };
runLater(incCounter, 1000).then(() => { return runLater(incCounter, 2000); }).then(() => { return runLater(incCounter, 3000); });
|
- 比起原來的巢狀結構,雖然程式碼變得更多行,但是把任務分得更清楚,更好看得懂也更好維護
Comments