原文:https://dev.to/bhagatparwinder/promises-chaining-error-handling-operators-3ccb
上篇文章詳細(xì)的介紹了什么是 promise 以及如何創(chuàng)建、 resolve 和 reject。
這一次,我們將討論 promise 中的鏈?zhǔn)讲僮饕约板e(cuò)誤處理和可用的運(yùn)算符。
鏈?zhǔn)?/h2>
回調(diào)函數(shù)最顯著的缺點(diǎn)之一是當(dāng)我們連接它們時(shí)形成的嵌套結(jié)構(gòu),在 then 的幫助下,我們可以創(chuàng)建一個(gè)更易閱讀、理解和調(diào)試的扁平結(jié)構(gòu)。
假設(shè)我們有一個(gè) waitForMe
的函數(shù)返回 promise,這個(gè)函數(shù)等待 2 秒后會(huì)返回你朋友的名字。
const waitForMe = function(name) { return new Promise((resolve, reject) => { setTimeout(() => { return resolve(name); }, 2000); });}waitForMe("Parwinder") .then((data) => { console.log(data); // Outputs/yells "Parwinder" after 2 second });
假設(shè)你有很多懶惰的朋友,因?yàn)槟愫苤毕攵冀o他們打電話。我們可以一個(gè)個(gè)的給他們打電話(鏈?zhǔn)讲僮鳎?/p>
waitForMe("Parwinder") .then((data) => { console.log(data); // waits 2 seconds and outputs "Parwinder" return waitForMe("Lauren"); }) .then((data) => { console.log(data); // waits another 2 seconds and outputs "Lauren" return waitForMe("Robert"); }) .then((data) => { console.log(data); // waits another 2 seconds and outputs "Robert" return waitForMe("Eliu"); }) .then((data) => { console.log(data); // waits another 2 seconds and outputs "Eliu" })
你會(huì)看到我們是如何用鏈?zhǔn)秸{(diào)用名字的以及控制臺(tái)間隔 2 秒打印出它們,每一個(gè) then
操作符會(huì)返回一個(gè) promise 然后和其他的 then
鏈起來(lái),同時(shí)保持代碼結(jié)構(gòu)的扁平。
錯(cuò)誤處理
在 promise 的鏈?zhǔn)街杏袃煞N方法可以處理錯(cuò)誤,要么在 then
塊中傳入錯(cuò)誤處理器或者使用 catch
操作符。我們已經(jīng)在前一篇文章中討論了第一種方法。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { reject("an error has occurred"); }, 2000)});myPromise.then((response) => { console.log(response);}, (error) => { console.log(error); // an error has occurred});
在上面的例子中,then
包含兩個(gè)回調(diào),第一個(gè)是成功的處理器,第二個(gè)是錯(cuò)誤處理器。使用這兩個(gè)處理器是完全沒(méi)有問(wèn)題的同時(shí)在多數(shù)情況下工作良好。它也有某些缺點(diǎn):
then
塊中添加錯(cuò)誤處理器。為了解決這些缺點(diǎn),我們使用 catch
操作符。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { reject("an error has occurred"); }, 2000)});myPromise.then((response) => { console.log(response);}).catch((error) => { console.log(error); // an error has occured});
在 promise 的鏈?zhǔn)秸{(diào)用中,我們可以這樣使用 catch
操作符:
const waitForMe = function (name) { return new Promise((resolve, reject) => { if (name === "Robert") { return reject("Robert is always on time"); } else { setTimeout(() => { return resolve(name); }, 2000); } });}waitForMe("Parwinder") .then((data) => { console.log(data); // wait 2 second and log "Parwinder" return waitForMe("Lauren"); }) .then((data) => { console.log(data); // wait 2 more seconds and log "Lauren" return waitForMe("Robert"); // this will result in promise rejection }) .then((data) => { console.log(data); // this never gets executed return waitForMe("Eliu"); }) .then((data) => { console.log(data); // this never gets executed }) .catch((error) => { console.log(error); // Robert is always on time })
記住在 promise 的鏈?zhǔn)秸{(diào)用中一旦有一個(gè)產(chǎn)生錯(cuò)誤后續(xù)的鏈將會(huì)被終止。這也是為什么最后兩個(gè)打印沒(méi)有執(zhí)行。
catch
操作符并不總是必須添加到最后,它可以添加到鏈?zhǔn)降闹虚g然后可以捕獲到它那個(gè)位置之前的錯(cuò)誤。
const waitForMe = function (name) { return new Promise((resolve, reject) => { if (name === "Robert") { return reject("Robert is always on time"); } else { setTimeout(() => { return resolve(name); }, 2000); } });}waitForMe("Parwinder") .then((data) => { console.log(data); // wait 2 second and log "Parwinder" return waitForMe("Lauren"); }) .then((data) => { console.log(data); // wait 2 more seconds and log "Lauren" return waitForMe("Robert"); // this will result in promise rejection }) .catch((error) => { // catches the promise rejection console.log(error); // Robert is always on time return waitForMe("Eliu"); // continues the chain }) .then((data) => { console.log(data); // Eliu })
注意: 為什么不一直使用 catch
而忽略 then
中的錯(cuò)誤處理器呢?
我上面提到過(guò) then 的劣勢(shì):
需要為每一個(gè) then 添加一個(gè)錯(cuò)誤處理器。
有時(shí)候你可能需要在鏈?zhǔn)?then
的錯(cuò)誤處理器中有不同的錯(cuò)誤處理方式,基于這一點(diǎn),then 中獨(dú)立的錯(cuò)誤處理器可能會(huì)更有優(yōu)勢(shì)。
操作符
promise 上有兩個(gè)重要的操作符,它們分別適應(yīng)特定的場(chǎng)景:Promise.all
和 Promise.race
。
Promise.all
當(dāng)你在一個(gè)異步操作后執(zhí)行另一個(gè)(串行),promise 的鏈?zhǔn)秸{(diào)用很順手。經(jīng)常,你需要多個(gè)異步操作并行執(zhí)行而不是等一個(gè)執(zhí)行完成后再執(zhí)行。另外,你的操作依賴所有的異步操作的完成情況。
Promise.all
使我們可以同時(shí)執(zhí)行多個(gè)異步操作,但依舊需要等到它們都完成 了才執(zhí)行回調(diào)。
const waitForMe = function (name) { return new Promise((resolve, reject) => { setTimeout(() => { return resolve(name); }, 2000); });}const firstPromise = waitForMe("Parwinder");const secondPromise = waitForMe("Lauren");const thirdPromise = waitForMe("Robert");const fourthPromise = waitForMe("Eliu");Promise.all([firstPromise, secondPromise, thirdPromise, fourthPromise]) .then((data) => { console.log(data); // [ 'Parwinder', 'Lauren', 'Robert', 'Eliu' ] });
上面的例子同時(shí)執(zhí)行了 promise,等到它們都返回 name 就會(huì)輸出一個(gè)結(jié)果的數(shù)組。這種方式執(zhí)行耗費(fèi) 2 秒,鏈?zhǔn)降男问絼t耗費(fèi) 8 秒來(lái)輸出四個(gè)名字。
數(shù)組中輸出順序是嚴(yán)格與輸入 Promise.all
中的順序是一致的。
注意: Promise.all
中即使有一個(gè)錯(cuò)誤產(chǎn)生,整個(gè)結(jié)果都會(huì)失敗。
const waitForMe = function (name) { return new Promise((resolve, reject) => { if (name === "Robert") { return reject("Robert is always on time"); } else { setTimeout(() => { return resolve(name); }, 2000); } });}const firstPromise = waitForMe("Parwinder");const secondPromise = waitForMe("Lauren");const thirdPromise = waitForMe("Robert");const fourthPromise = waitForMe("Eliu");Promise.all([firstPromise, secondPromise, thirdPromise, fourthPromise]) .then((data) => { console.log(data); }) .catch((error) => { console.log(error); // Robert is always on time })
它會(huì)忽略其他成功的 promise,若有多個(gè)錯(cuò)誤它會(huì)返回輸入 Promise.all
中數(shù)組的第一個(gè)發(fā)生錯(cuò)誤的 promise。
const waitForMe = function (name) { return new Promise((resolve, reject) => { if (name === "Robert") { return reject("Robert is always on time"); } else if (name === "Lauren") { return reject("Lauren is always on time"); } else { setTimeout(() => { return resolve(name); }, 2000); } });}const firstPromise = waitForMe("Parwinder");const secondPromise = waitForMe("Lauren");const thirdPromise = waitForMe("Robert");const fourthPromise = waitForMe("Eliu");Promise.all([firstPromise, secondPromise, thirdPromise, fourthPromise]) .then((data) => { console.log(data); }) .catch((error) => { console.log(error); // Lauren is always on time })
Promise.race
Promise.race
處理一個(gè)特殊的情形,當(dāng)你需要同時(shí)執(zhí)行多個(gè)異步操作,但不需要等到它們?nèi)客瓿伞O喾矗阆氘?dāng)?shù)谝粋€(gè) promise 完成后盡快執(zhí)行回調(diào)。
const waitForMe = function (name, time) { return new Promise((resolve, reject) => { setTimeout(() => { return resolve(name); }, time); });}const firstPromise = waitForMe("Parwinder", 4000);const secondPromise = waitForMe("Lauren", 3000);const thirdPromise = waitForMe("Robert", 7000);const fourthPromise = waitForMe("Eliu", 5000);Promise.race([firstPromise, secondPromise, thirdPromise, fourthPromise]) .then((data) => { console.log(data); // Lauren }) .catch((error) => { console.log(error); })
我為 setTimeout
添加了一個(gè)參數(shù),跟著每一個(gè)名字我傳入了不同的時(shí)間,"Lauren" 只有 3 秒鐘所以她永遠(yuǎn)會(huì)贏得"比賽",然后打印出她的名字。