35 – Promises:链式、错误处理和运算符

原文:https://dev.to/bhagatparwinder/promises-chaining-error-handling-operators-3ccb

上篇文章详细的介绍了什么是 promise 以及如何创建、 resolve 和 reject。

这一次,我们将讨论 promise 中的链式操作以及错误处理和可用的运算符。

链式

回调函数最显著的缺点之一是当我们连接它们时形成的嵌套结构,在 then 的帮助下,我们可以创建一个更易阅读、理解和调试的扁平结构。

假设我们有一个 waitForMe 的函数返回 promise,这个函数等待 2 秒后会返回你朋友的名字。

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    });

假设你有很多懒惰的朋友,因为你很着急想都给他们打电话。我们可以一个个的给他们打电话(链式操作)。

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"    })

你会看到我们是如何用链式调用名字的以及控制台间隔 2 秒打印出它们,每一个 then 操作符会返回一个 promise 然后和其他的 then 链起来,同时保持代码结构的扁平。

错误处理

在 promise 的链式中有两种方法可以处理错误,要么在 then 块中传入错误处理器或者使用 catch 操作符。我们已经在前一篇文章中讨论了第一种方法。

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 包含两个回调,第一个是成功的处理器,第二个是错误处理器。使用这两个处理器是完全没有问题的同时在多数情况下工作良好。它也有某些缺点:

  • 如果成功处理器中产生了错误,你将无法捕获或处理它;
  • 如果你像上面的链式例子一样使用链式调用,你需要在每个 then 块中添加错误处理器。
  • 为了解决这些缺点,我们使用 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 的链式调用中,我们可以这样使用 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 的链式调用中一旦有一个产生错误后续的链将会被终止。这也是为什么最后两个打印没有执行。

    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    })    .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 中的错误处理器呢?

    我上面提到过 then 的劣势:

    需要为每一个 then 添加一个错误处理器。

    有时候你可能需要在链式 then 的错误处理器中有不同的错误处理方式,基于这一点,then 中独立的错误处理器可能会更有优势。

    操作符

    promise 上有两个重要的操作符,它们分别适应特定的场景:Promise.allPromise.race

    Promise.all

    当你在一个异步操作后执行另一个(串行),promise 的链式调用很顺手。经常,你需要多个异步操作并行执行而不是等一个执行完成后再执行。另外,你的操作依赖所有的异步操作的完成情况。

    Promise.all 使我们可以同时执行多个异步操作,但依旧需要等到它们都完成 了才执行回调。

    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' ]    });

    上面的例子同时执行了 promise,等到它们都返回 name 就会输出一个结果的数组。这种方式执行耗费 2 秒,链式的形式则耗费 8 秒来输出四个名字。

    数组中输出顺序是严格与输入 Promise.all 中的顺序是一致的。

    注意: Promise.all 中即使有一个错误产生,整个结果都会失败。

    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    })

    它会忽略其他成功的 promise,若有多个错误它会返回输入 Promise.all 中数组的第一个发生错误的 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 处理一个特殊的情形,当你需要同时执行多个异步操作,但不需要等到它们全部完成。相反,你想当第一个 promise 完成后尽快执行回调。

    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 添加了一个参数,跟着每一个名字我传入了不同的时间,"Lauren" 只有 3 秒钟所以她永远会赢得"比赛",然后打印出她的名字。

    你可能感兴趣的:(35 – Promises:链式、错误处理和运算符)