ES2015 筆記(4) Promise

透過ES2015建立Promise物件,讓你的JavaScript程式碼更容易閱讀,更好維護

說實在一開始看到JavaScript的Promise寫法

腦中浮現的想法就是"這不就是 linq 嗎"

自然也不覺得陌生,了解後反而覺得這種寫法很好閱讀也很方便

什麼是Promise,什麼時候需要

會需要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,共會有三種狀態

  1. 第一個就是你new出來Promise這個物件時會呈現於pending狀態
  2. 第二個就是fulfill的狀態,這個就會是走到then的語法
  3. 第三個就是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上了