透過ES2015建立Promise物件,讓你的JavaScript程式碼更容易閱讀,更好維護
說實在一開始看到JavaScript的Promise寫法
腦中浮現的想法就是"這不就是 linq 嗎"
自然也不覺得陌生,了解後反而覺得這種寫法很好閱讀也很方便
會需要Promise的寫法,主要原因是我們不想要讓程式Hang在某一段程式,其作用就像async
在解釋Promise的運作前,先看一下以下的例子
若是BuildPayList會花很久的時間,假若是數秒以上,我們就會希望用到Promise的作法
所以程式碼在等待BuildPayList完成前會繼續往下執行
所以你會有看到finsih的訊息會先被輸出,然後才會回去執行 then 裡面的東西
BuildPayList(myMoney)
//check is money is enough to pay all
.then(CheckIsAblePayAll)
console.log("finish");
在New Promise時,會有內建兩個方法分別是resolve 跟 reject,共會有三種狀態
- 第一個就是你new出來Promise這個物件時會呈現於pending狀態
- 第二個就是fulfill的狀態,這個就會是走到then的語法
- 第三個就是reject的狀態,這邊就會執行到catch的語法
情境如下,我們需要建立一個付帳清單,然後再去檢查我們身上的錢是否足以支付這個清單
如何使用Promise的機制寫出易懂的程式碼
首先我們有一個BuildPayList的function來建立Promise物件
resolve時回傳的物件為 {money, PayList [{ item, cost}] }
reject 時回傳的物件為 { Success, Message}
function BuildPayList(money = 0){
return new Promise(function (resolve, reject){
if (money > 0)
resolve({money, PayList: [{item:"food", cost:100}, {item:"insurance", cost:1500} ], payLog:[]});
else
reject({Success: false, Message: "You don't any money to pay!"});
});
}
接著我們再建立一個function用來檢查是否身上的錢足夠支付清單,這邊的物件架構為上述resolve時所回傳的物件
function CheckIsAblePayAll(obj){
let tempMoney = obj.money
for (let pay of obj.PayList){
tempMoney-= pay.cost;
if (tempMoney < 0 )
throw "You don't have enough money to pay all items!";
}
return obj;
}
最後則是,若是能夠支付所有清單,則實行付錢的行為,這裡的參數跟CheckIsAblePayAll裡用的是一樣的
付完帳後,我們則把付帳過程的記錄跟剩下的金錢回傳 {payLog [ ], RestMoney}
function PayAll(obj){
for (let pay of obj.PayList)
{
obj.payLog.push(`You have ${obj.money}, spend ${pay.cost} on ${pay.item}, remaining ${obj.money - pay.cost}`);
obj.money -= pay.cost;
}
return {payLog:obj.payLog, restMoney: obj.money};
}
當我們把所有的function都寫完時,這時候的重頭戲就是我們如何使用Promise寫出易讀的程式碼了
let myMoney = 2000;
BuildPayList(myMoney)
//check is money is enough to pay all
.then(CheckIsAblePayAll)
.then(PayAll)
//display all pay log
.then((result)=> {
for (let log of result.payLog)
console.log(log);
console.log(`Finally, you have ${result.restMoney} on hand`);
})
.catch((error)=> console.log(error));
我們來看看Promise是怎麼運作的
一開始當我們把手頭上的2000元當作參數當參數呼叫BuildPayList(2000)時
會先得到一個Promise的物件,在執行到呼叫resolve或是reject時,Promise都是處於pending狀態
直到判斷我們手頭上的現金大於0時,即會執行resolve的方法,才會由pending轉為fulfilled
若是一開始money小於0,則會走向rejected,即會執行catch
當物件建立起,即開始執行第一個 then ,即為CheckIsAblePayAll
在CheckIsAblePayAll中也會檢查手頭的錢是否夠付,若是夠付(resolve),則將一開始建立的物件回傳
不夠付則throw excpetion,即為走向catch (reject)
在夠付的情況下,執行第二個then 執行付錢的動作,最後將payLog與restMoney傳回
最後一個then則是一個arrow function,負責將payLog呈現出來,並將剩下的錢輸出
最後一行的catch則表式,這個Promise的物件一個接一個的then執行中,若狀態變成rejected,則會執行
執行的結果如下
1. money=2000
You have 2000, spend 100 on food, remaining 1900
You have 1900, spend 1500 on insurance, remaining 400
Finally, you have 400 on hand
2. money=1000 (在執行到第一個then就會throw exception到catch了)
You don't have enough money to pay all items!
3. money=0 (連第一個then都還沒執行到,在new Promise時就被引至reject了)
Object {sucess: false, Message: "You don't any money to pay!"}
看完這個好東西,你應該會等不及想要把Promise物件應用到你的JavaScript上了