35 – Promises:鏈?zhǔn)健㈠e(cuò)誤處理和運(yùn)算符

原文: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):

  • 如果成功處理器中產(chǎn)生了錯(cuò)誤,你將無(wú)法捕獲或處理它;
  • 如果你像上面的鏈?zhǔn)嚼右粯邮褂面準(zhǔn)秸{(diào)用,你需要在每個(gè) 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.allPromise.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ì)贏得"比賽",然后打印出她的名字。

    ?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
    平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

    推薦閱讀更多精彩內(nèi)容