Promise.all中的错误处理(转于慕课)

当我们想“并发执行”若干个任务的时候,我们很容易就想到了Promise.all。但是Promise.all有一个缺陷:当有一个任务失败的时候,就会直接进入catch的逻辑了。这个可能并不是我们想要的结果。想象一下我们同时发出了3个网络请求,其中2个正常返回了,有一个reject了,结果却都挂了,只能被迫进入错误处理的逻辑。我们的需求是:出错的那一个请求不会影响到正常的请求。这种情况应该怎么处理呢?

使用await,增加try…catch…逻辑
要解决上面的问题,思路很简单,只需要再外面再包一层Promise就行了,不管内部的Promise是resolved或者rejected了,外层的Promise都resolve就可以了。这样,Promise.all接收到的,永远都是resolved的Promise。两层的Promise看起来有些别扭,为了代码写起来稍微好看一点,我们用await和try…catch来处理。

/**
 * @param {Promise} p 
 */
async function promiseWithError(p) {
  try {
    const res = await p;
    return {
      err: 0,
      data: res
    };
  } catch(e) {
    return {
      err: 1
    }
  }
}

下面,通过示例代码来看一下。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('promise resolve 1');
  }, 1000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('promise resolve 2');
  }, 2000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('promise reject 3');
  }, 3000);
});

Promise.all([p1, p2, p3])
  .then(res => {
    console.log('resolve:', res);
  })
  .catch(err => {
    console.log('reject:', err);
  });

// 最终输出为: promise reject 3

我们修改一下代码:

Promise.all([p1, p2, p3].map(item => promiseWithError(item)))
  .then(res => {
    console.log('resolve:', res);
  })
  .catch(err => {
    console.log('reject:', err);
  });


// 最终的输出为:
// resolve: [{ err: 0, data: "promise resolve 1"}, { err: 0, data:"promise resolve 2"}, { err: 1 }]

我们用promiseWithError把原本的promise包了一层。async可以把任何函数都转换为Promise,最终传入Promise.all的依然是一个Promise数组,没有问题。

这样,我们就解决了问题:Promise.all不会因为其中某一个Promsie被reject而导致整个挂掉。

Promise.allSettled
要解决文章开头提到的问题,我们还可以使用Promise.allSettled。

Promise.allSettled()方法返回一个在所有给定的promise已被resolve或被reject后的promise,并带有一个对象数组,每个对象表示对应的promise结果。依然使用上面的例子:

Promise.allSettled([p1, p2, p3])
		.then(res => {
			console.log('resolve:', res);
		})
		.catch(err => {
			console.log('reject:', err);
		});

// 最终输出为:
// resolve: [{ status: "fulfilled", value: "promise resolve 1" }, { status: "fulfilled", value: "promise resolve 2"}, { status: "rejected", reason: "promise reject 3"} ]

目前,Promise.allSettled处于 tc39 stage-4 阶段,马上就会正式发布了。

写在后面
本文阐述了两种处理Promise.all中错误的方法,对日常开发是很有用处的。关于JavaScript中的异步任务相关总结,可以查看我之前的文章:

https://www.imooc.com/article/297825

https://www.imooc.com/article/303152

作者:Coy_Pan
链接:https://www.imooc.com/article/303153
来源:慕课网
本文首次发布于慕课网 ,转载请注明出处,谢谢合作

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Promise.all() 方法用于将多个 Promise 实例包装成一个新的 Promise 实例。如果所有的 Promise 实例都变为fulfilled状态,Promise.all() 返回的实例才会变为fulfilled状态,返回值是一个数组,包含所有 Promise 实例的返回值。如果其一个 Promise 实例变为rejected状态,Promise.all() 返回的实例就会变为rejected状态,返回值是第一个被rejected的 Promise 实例的返回值。 如果需要在 Promise.all() 修改某个 Promise 实例的状态,可以使用 Promise.race() 方法。Promise.race() 方法同样接受一个 Promise 实例的数组作为参数,并返回一个新的 Promise 实例。不同的是,Promise.race() 方法只要有一个 Promise 实例变为fulfilled状态或者rejected状态,就会返回相应状态的结果,并忽略其他 Promise 实例的状态。 因此,可以将需要修改状态的 Promise 实例与一个永远不会变为fulfilled状态或者rejected状态的 Promise 实例一起传递给 Promise.race() 方法,这样只要修改了需要修改的 Promise 实例的状态,Promise.race() 就会返回相应的结果。例如,以下代码演示了如何修改 Promise.all() 某个 Promise 实例的状态: ```javascript const promise1 = Promise.resolve(1); const promise2 = Promise.reject(new Error('error')); const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 1000, 'resolved'); }); Promise.race([promise2, new Promise(() => {})]).catch(() => { // 修改 promise1 的状态为 rejected Promise.resolve(promise1).then(() => { throw new Error('error'); }); }); Promise.all([promise1, promise2, promise3]).then((values) => { console.log(values); }).catch((error) => { console.error(error); }); ``` 在该代码,我们使用了 Promise.race() 方法将 promise2 和一个永远不会变为fulfilled状态或者rejected状态的 Promise 实例一起传递给它。在 Promise.race() ,我们使用 catch() 方法捕获了 promise2 的 rejected 状态,并在 catch() 方法修改了 promise1 的状态为 rejected。由于 Promise.race() 只要有一个 Promise 实例变为fulfilled状态或者rejected状态就会返回相应的结果,因此,当 promise1 的状态被修改为 rejected 时,Promise.race() 返回该状态的结果,从而使得 Promise.all() 也变为 rejected 状态,最终输出错误信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值