iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0
Modern Web

JavaScript 之路,往前邁進吧!系列 第 15

JS之路 Day15 - Promise Chain(承諾鏈)

  • 分享至 

  • xImage
  •  

前言

有時候很常見會有情況是需要很多的非同步操作,每一個非同步的後續操作都建立在前面的非同步操作的成功,所以會繼續上一步的結果,在使用callback處理這種狀況的時候,就會產生可怕的callback hell,而Promise Chain可以很好的改善這個問題,就讓我們看下去。

promise chain的使用方式

從看一個例子來了解使用方式。

const promise = new Promise(function (resolve) {
  resolve(1);
});

promise
  .then(function (result) {
    console.log(result); // 1
    return result + 1;
  })
  .then(function (result) {
    console.log(result); // 2 
    return result + 1;
  })
  .then(function (result) {
    console.log(result); // 3
    return result + 1;
  })
  .then(function (result) {
    console.log(result); // 4 
    return result + 1;
  });

這是使用了promise方式來處理的promise chain,藉由.then會回傳一個新的 promise,讓每一個在promise chainpromise都代表上一個非同步的步驟已經完成。

注意,return之後的東西是新的promise,所以上一個流程已經結束了,就像是有人等烤吐司機烤完吐司,才去啟動微波爐加熱便當,那麼當他已經走到微波爐準備加熱便當時,就代表烤土司機已經烤完吐司了,雖然這兩個步驟都需要等待,但是不會一起等是獨立的,上一個等完才換下一個等。

而在這個例子中,promise chain運行的流程如下:

  1. 初始 promise 會直接把resolve結果為1。
  2. 然後 .then 開始作用,會創建一個新的promise,以上一個的return作為值,在這裡是1。
  3. 下一個.then就會獲得上一個.then的值(2),再進行處理之後,會將值傳遞給下一個處理階段。
  4. ... 以此類推。

這邊其實會比較沒有非同步的感覺沒有等待,可能用setTimeout的方式似乎比較好,但只要使用promise就一定會使用非同步的方式去進行的,內部的狀態還是會從pending未確認的狀態,變成Fulfilled,而每一次的.then就相當於重新判定一次。

再次強調,這樣做之所以可行的核心是因為每一個then()後都會回傳一個新的promise

A做完後,使用then()會回傳新的B,然後再用B去做事,做完後再用then()回傳一個新的C,這種一層一層傳遞下去,就像是一個鏈子一樣,所以會稱之為Promise Chain

這裡會引申出一個新的問題,那就是如果把很多的then()都寫在同一個promise那還叫做Promise Chain嗎?

像是這樣:

let promise = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(1), 500);
});

promise.then(function (result) {
  console.log(result);
  return result * 3;
});

promise.then(function (result) {
  console.log(result);
  return result * 5;
});

promise.then(function (result) {
  console.log(result);
  return result * 10;
});

這樣的話依舊是Promise,依然使用著非同步的方式,但其實就不是Promise Chain,它們沒有連再一起,就只是各自做各自的事情,互不關聯,沒有延續所以也就不會互相傳遞result,所以在這個例子來說,會發現全部印出來的東西都是1。

我有想到了一個例子:

Promise Chain

  1. 如果吃完蘋果的話,休息五分鐘就去吃香蕉
  2. 如果吃完蘋果後跑去吃完香蕉,休息五分鐘就去吃西瓜
  3. 如果吃完蘋果後跑去吃完香蕉又去吃了西瓜,休息五分鐘就準備去廁所

Promise
 1. 如果吃完蘋果的話,休息五分鐘就去吃香蕉
 2. 如果吃完蘋果的話,休息五分鐘就去吃西瓜
 3. 如果吃完蘋果的話,休息五分鐘去去廁所

回傳解決與拒絕

前面一直在用then()來當作Promise Chain的下一個回傳,但其實連接的概念除了then()之外還有catch(),差別在於Promise有沒有被拒絕,當一個Promise被解決時,也就是狀態是fulfilled,那麼就會使用then()返回已經解決的Promise,如果是被拒絕時,也就是狀態rejected,那麼就會使用catch()來回傳,舉一個例子:

function promise(a) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (judge) {
        resolve(`成功`);
      } else {
        reject("你失敗了謝謝");
      }
    }, 0);
  });
}

promise(1) // 第一次成功,因為是true
  .then((number) => {
    console.log(number);
    return promise(1); // 第二次成功,因為是true
  })
  .then((number) => {
    console.log(number);
    return promise(0); // 失敗,下面的就不會去看,會直接跳catch
  })
  .then((number) => {
    console.log(number);
    return promise(1);
  })
.then((number) => {
    console.log(number);
    return promise(1234567890); // 不會有反應了
  })
  .catch((number) => {
    console.log(number);
  });

首先,要在自己寫的promise同時存在成功跟失敗的結果,要用成判斷式的型態,因為狀態是不可逆的,pending只要到了fulfilled或是rejected其中一個就再也回不去了。

然後這裡我寫的判斷是,要是值是true的時候就會成功,false的話就會你失敗了謝謝,而正數的時候值會是true,負數時值會是false,下方的呼叫寫了三個then()跟一個catch(),這裡要測試的是當不管是幾個then(),只要有一個條件不成立,值是false,那麼就直接會跳到catch()那裡去,所以會發現這個程式的結果是:

成功
成功
你失敗了謝謝

第一個then()那裡回傳出去的值就已經是負數了,代表會是false,所以後面的then()都不用看,會直接到最後的catch()結果,而如果想要再catch()再開始繼續也是可以,就直接再傳一個新的promise結果即可。

 .catch((r) => {
    console.log(r);
    return promise(1);
  })
  .then((r) => {
    console.log(r); // 成功
  });

總結

除了用then來串鏈形成Promise Chain的方式來處理很多個連續的非同步操作外,也有一種Promise的語法糖,叫做async/await的方式,因為原理還是Promise,所以是語法糖,關於這個以後會專門做一期文章給大家講解。

reference

[1] MDN - Using Promises
[2] Promises chaining


上一篇
JS之路 Day14 - Promise(承諾)
下一篇
JS之路 Day16 - Promise methods(承諾方法)
系列文
JavaScript 之路,往前邁進吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言