JS之路 Day15 - Promise Chain(承諾鏈)
前言
有時候很常見會有情況是需要很多的非同步操作,每一個非同步的後續操作都建立在前面的非同步操作的成功,所以會繼續上一步的結果,在使用callback
處理這種狀況的時候,就會產生可怕的callback hell
,而Promise Chain
可以很好的改善這個問題,就讓我們看下去。
promise chain 的使用方式
從看一個例子來了解使用方式。
1 | const promise = new Promise(function (resolve) { |
這是使用了promise
方式來處理的promise chain
,藉由.then
會回傳一個新的 promise
,讓每一個在promise chain
的promise
都代表上一個非同步的步驟已經完成。
注意,return
之後的東西是新的promise
,所以上一個流程已經結束了,就像是有人等烤吐司機烤完吐司,才去啟動微波爐加熱便當,那麼當他已經走到微波爐準備加熱便當時,就代表烤土司機已經烤完吐司了,雖然這兩個步驟都需要等待,但是不會一起等是獨立的,上一個等完才換下一個等。
而在這個例子中,promise chain
運行的流程如下:
- 初始 promise 會直接把 resolve 結果為 1。
- 然後 .then 開始作用,會創建一個新的 promise,以上一個的 return 作為值,在這裡是 1。
- 下一個.then 就會獲得上一個.then 的值(2),再進行處理之後,會將值傳遞給下一個處理階段。
- … 以此類推。
這邊其實會比較沒有非同步的感覺沒有等待,可能用setTimeout
的方式似乎比較好,但只要使用promise
就一定會使用非同步的方式去進行的,內部的狀態還是會從pending
未確認的狀態,變成Fulfilled
,而每一次的.then
就相當於重新判定一次。
再次強調,這樣做之所以可行的核心是因為每一個 then()後都會回傳一個新的promise
。
A 做完後,使用 then()會回傳新的 B,然後再用 B 去做事,做完後再用 then()回傳一個新的 C,這種一層一層傳遞下去,就像是一個鏈子一樣,所以會稱之為Promise Chain
。
這裡會引申出一個新的問題,那就是如果把很多的 then()都寫在同一個promise
那還叫做Promise Chain
嗎?
像是這樣:
1 | let promise = new Promise(function (resolve, reject) { |
這樣的話依舊是Promise
,依然使用著非同步的方式,但其實就不是Promise Chain
,它們沒有連再一起,就只是各自做各自的事情,互不關聯,沒有延續所以也就不會互相傳遞result
,所以在這個例子來說,會發現全部印出來的東西都是 1。
我有想到了一個例子:
Promise Chain
- 如果吃完蘋果的話,休息五分鐘就去吃香蕉
- 如果吃完蘋果後跑去吃完香蕉,休息五分鐘就去吃西瓜
- 如果吃完蘋果後跑去吃完香蕉又去吃了西瓜,休息五分鐘就準備去廁所
Promise - 如果吃完蘋果的話,休息五分鐘就去吃香蕉
- 如果吃完蘋果的話,休息五分鐘就去吃西瓜
- 如果吃完蘋果的話,休息五分鐘去去廁所
回傳解決與拒絕
前面一直在用 then()來當作Promise Chain
的下一個回傳,但其實連接的概念除了 then()之外還有 catch(),差別在於Promise
有沒有被拒絕,當一個Promise
被解決時,也就是狀態是fulfilled
,那麼就會使用 then()返回已經解決的Promise
,如果是被拒絕時,也就是狀態rejected
,那麼就會使用 catch()來回傳,舉一個例子:
1 | function promise(a) { |
首先,要在自己寫的promise
同時存在成功跟失敗的結果,要用成判斷式的型態,因為狀態是不可逆的,pending
只要到了fulfilled
或是rejected
其中一個就再也回不去了。
然後這裡我寫的判斷是,要是值是 true 的時候就會成功,false 的話就會你失敗了謝謝,而正數的時候值會是 true,負數時值會是 false,下方的呼叫寫了三個 then()跟一個 catch(),這裡要測試的是當不管是幾個 then(),只要有一個條件不成立,值是 false,那麼就直接會跳到 catch()那裡去,所以會發現這個程式的結果是:
1 | 成功 |
第一個 then()那裡回傳出去的值就已經是負數了,代表會是 false,所以後面的 then()都不用看,會直接到最後的 catch()結果,而如果想要再 catch()再開始繼續也是可以,就直接再傳一個新的promise
結果即可。
1 | .catch((r) => { |
總結
除了用then
來串鏈形成Promise Chain
的方式來處理很多個連續的非同步操作外,也有一種Promise
的語法糖,叫做 async/await 的方式,因為原理還是Promise
,所以是語法糖,關於這個以後會專門做一期文章給大家講解。