有時候很常見會有情況是需要很多的非同步操作,每一個非同步的後續操作都建立在前面的非同步操作的成功,所以會繼續上一步的結果,在使用callback
處理這種狀況的時候,就會產生可怕的callback hell
,而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 chain
的promise
都代表上一個非同步的步驟已經完成。
注意,return
之後的東西是新的promise
,所以上一個流程已經結束了,就像是有人等烤吐司機烤完吐司,才去啟動微波爐加熱便當,那麼當他已經走到微波爐準備加熱便當時,就代表烤土司機已經烤完吐司了,雖然這兩個步驟都需要等待,但是不會一起等是獨立的,上一個等完才換下一個等。
而在這個例子中,promise chain
運行的流程如下:
這邊其實會比較沒有非同步的感覺沒有等待,可能用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
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
,所以是語法糖,關於這個以後會專門做一期文章給大家講解。
[1] MDN - Using Promises
[2] Promises chaining