前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >35 - Promises:链式、错误处理和运算符​

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

作者头像
前端黑板报
发布2022-12-01 17:05:08
4070
发布2022-12-01 17:05:08
举报
文章被收录于专栏:前端黑板报前端黑板报

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

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

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

链式

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

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

代码语言:javascript
复制
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
    });

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

代码语言:javascript
复制
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 操作符。我们已经在前一篇文章中讨论了第一种方法。

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

  1. 1. 如果成功处理器中产生了错误,你将无法捕获或处理它;
  2. 2. 如果你像上面的链式例子一样使用链式调用,你需要在每个 then 块中添加错误处理器。

为了解决这些缺点,我们使用 catch 操作符。

代码语言:javascript
复制
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 操作符:

代码语言:javascript
复制
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 操作符并不总是必须添加到最后,它可以添加到链式的中间然后可以捕获到它那个位置之前的错误。

代码语言:javascript
复制
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 使我们可以同时执行多个异步操作,但依旧需要等到它们都完成 了才执行回调。

代码语言:javascript
复制
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 中即使有一个错误产生,整个结果都会失败。

代码语言:javascript
复制
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。

代码语言:javascript
复制
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 完成后尽快执行回调。

代码语言:javascript
复制
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 秒钟所以她永远会赢得"比赛",然后打印出她的名字。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端黑板报 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 链式
  • 错误处理
  • 操作符
    • Promise.all
      • Promise.race
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档