JS 原力覺醒 Day14 - 一生懸命的約定:Promise

上一章節我們提到有一些 JS 的 Web API 會需要在「背景執行」,同時又不影響整個網頁主程式的運行,這些 API 利用瀏覽器 Event Queue 的機制來達成這個目的,也就是所謂非同步的動作。不過難道只有在使用 這些 Web API 的時候,才能使用到非同步的行為嗎?我們有沒有可能讓自己寫的功能,也具有非同步的行為呢?

答案是,可以的,只是方式不太一樣,如果想要讓自己寫的功能也具有非同步的行為,我們會需要用到今天要討論的主角 — Promise 。

Outline

  • Promise 簡介
  • Promise : 成敗之間
  • 成功的 Promise : Succeed and then
  • 失敗的 Promise : Catch with an error
  • Promise 概念圖
  • Promise : 一個生活化的例子

Promise 簡介

Promise 是什麼呢?以語法字面上的意義來看,用比較白話的方式解釋的話有一種:「我承諾幫你做某件事情,能不能成功還不一定,但是我做完之後會把結果告訴你」的意思。

那麼來看看比較技術層面的定義,在官方文件中的定義則是:

Promise 是一個代表非同步運作的最終狀態的物件 (成功或失敗)

A Promise is an object representing the eventual completion or failure of an asynchronous operation. (MDN)

雖然技術文件的解釋就顯得比較抽象,不過從上面看得出來 Promise 在 JS 裡面是以物件的方式存在,那麼接下來我們就來看看要怎麼使用 Promise 吧,基本的 Primise 宣告方式如下:

let promise = new Promise((resolve, reject) => {
  // executor code
}) 

我們以 Callback 的方式來告訴 Promise ,接下來我們定義的非同步函式要做什麼事情,而且也必須跟 Promise 說,做完想做的事情,得到結果後,怎樣的結果算是成功,怎樣的結果算是失敗?這些都會被記錄在這個 Promise 物件裡面,Promise 物件裡面有幾個相關屬性:

  1. state (狀態) :一個 Promise 裡一共會有三種狀態:
    • fulfilled :成功的狀態
    • rejected:失敗的狀態
    • pending :還在執行中的狀態
  2. result : 執行完 Promise 後的結果值

Promise : 成敗之間

那要定義 Promise 的運行結果? 你可以看到在 Callback 函式內有兩個引數,分別是 resolve 跟 reject ,就是由 JS 提供、用來決定 Promise 結果狀態時使用的兩個函式 :

  1. resolve 用在 Promise 成功且結果如預期時,呼叫這個函式會把 Promise 的 state 設為 fulfilled ,將執行結果數值傳入這個函式會讓上述提到的 Promise 的 result 設為給定的值。

    什麼意思呢?下面的程式碼就是一個 Promise 成功,並且把 result 設為 'Success' 的範例:

    let promise = new Promise((resolve, reject) => {
    	 resolve(' Success ') 
    })  
  1. reject 則與 resolve ,呼叫 reject 會將 state 設為 rejected ,意即失敗。

    let promise = new Promise((resolve, reject) => {
    if(someValueIwant){
    //do other things and resolve
    }else{
    reject(‘Failure’)
    }
    })

成功的 Promise : Succeed and then

寫到這邊有個要注意的重點是,上述提到 Promise 的兩個值 state 跟 result 是沒有辦法直接被取用的,他們只能透過某種方式被取用。所以這邊要講的是 then 函式,then 是指在 Promise 順利執行完成後,要取得結果值的方法。

在前面我們提到,Promise 的 callback 內,我們可以將取得的結果值丟給 resolve 函式,之後我們就可以夠過 .then 來取得這個結果,然後做其他事情。then 函式 一樣接收的是一個 callback ,並且帶有一個參數,這個參數就是 Promise 剛剛計算完的結果,以上述例子為例的話就像這樣:

let promise = new Promise((resovlve,reject)=>{
	//after some calculation
	let result  = 'value from some where'
	resolve(result) 
}) 

promise.then(result => {
		//use result to do something 
}) 

而為什麼要使用 .then 與 callback 的方式呢?因為這樣一來,JS 可以保證這個 callback 在 Promise 執行完之後才被呼叫。

失敗的 Promise : Catch with an error

如果一個 Promise 因為某些原因而被 reject ,那麼上面提到的 .then 裡的 calback 就不會被執行,相反的,他會執行另外一個 callback — 在 .catch 函式內被傳入的 callback。這裡提到的 catch 的用途有點像是在捕捉錯誤時的語法:try & catch 裡的 catch 部分,都是用在錯誤發生時。

 let promise = new Promise((resovlve,reject)=>{
	//after some calculation
	let error = 'some error happended!!'
  if (!result){
		reject( error ) 
	} 
}) 

promise.catch(error => {
	// log the error
})  

Promise 概念圖

「狀態」的概念對使用 Promise 來說是很重要的事情,那麼讓我用一張簡單的狀態圖來表示運行的順序吧,首先 Promise 會有一段執行的時間,所以直到剛剛說的 resolve 函式被執行之前,狀態都會是 pending ,而在這之後如果 resolve 被順利呼叫,Promise 的狀態就會變成 fulfilled ,否則就會是 rejected:

https://ithelp.ithome.com.tw/upload/images/20190929/20106580vaLn6I6Vvn.jpg

Promise : 一個生活化的例子

前面提到,一個 Promise 會有三種狀態:fulfilled 、reject 與 pending 。其實在我們生活中就常常遇到這樣的例子,那就是提款機啦!回想一下剛才提到的「狀態」,提款機其實剛好就有剛剛說的三種狀態可以類比到 Promise 上面!

使用提款機送出提款的要求時,會需要等待一段時間,這時候可以看成 Promise 的執行時間,也就是 pending ,那麼在執行完畢後,可能會發生兩種結果:一種是沒什麼問題 ( fulfilled 的狀態 ),提款機就直接吐錢出來 ( then );另一種是你的餘額不夠,那麼 ATM 直接進入 rejected 拒絕讓你提款,並解顯示錯誤訊息( catch )。

https://ithelp.ithome.com.tw/upload/images/20190929/2010658031IXCxGbUr.jpg

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×