前言
在 JavaScript 中,Promise 已经是必不可少的元素了。因为在异步操作上,Promise 比传统的回调函数要更直观、可维护性更高,而且可以解决回调地狱(callback hell)的问题。
在ES6之前,Promise对象的设计遵循了Promises/A+规范,但在ES6中,Promises/A+规范完全被全局Promise替代。
在最新版本的ECMAScript中,Promise出现了新的API——Promise.allSettled(即Promise.allSettled()方法)。那么,接下来我们就来详细学习一下Promise.allSettled吧!
Promise 基础
在ES6之前,处理异步操作的方式通常是通过回调函数来实现:
getUser(function(user, err) { if (err) { console.log(err); return; } console.log(user); });
但是这种处理异步操作的方式有一个明显的缺点,就是代码嵌套太深,导致可读性非常差。
而 Promise 可以简单而优雅的解决这个问题。我们可以简单地使用 Promise 来封装异步操作:
-- -------------------- ---- ------- --------- -------------------- - ------------------ -- -------------------- - ----------------- --- -------- --------- - ------ --- ------------------------- ------- - -- ---- -- --------- - -------------- - ---- - ------------ - --- -
上述代码相比于回调函数要清晰简单很多。如果出错,也可以通过 catch 捕捉到错误。
Promise.all
Promise.all 方法比较常用,在处理多个 Promise 实例时可以把多个 Promise 实例的值混合处理。Promise.all 接受一个 Promise 实例的数组作为参数,当接收到多个 Promise 实例返回值之后,会将所有实例的返回结果以数组形式返回。
-- -------------------- ---- ------- ------------------------ ----------- ------------ ----------------------- - --------------------- --- -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ----------- -- ----- --- - -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ----------- -- ----- --- - -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ---------- -- ----- --- -
在上述代码中,getUser1、getUser2、getUser3 都是异步函数,分别在 500 毫秒后分别正常返回了数字 1、2 和返回错误 3.
而在 Promise.all 的 then 函数中,我们得到了一个包含 1、2 的数组,同时用户的错误也被 catch 到了。
Promise.allSettled
与 Promise.all 不同,Promise.allSettled 方法会等待所有 Promise 实例都返回对应的状态(不论是 resolved 还是 rejected),只有所有实例的状态都被转化为 settled 后,Promise.allSettled 才会执行。
例如,在下面这个例子中,getUser1、getUser2、getUser3 都返回了正确的值,但是在 getUser4 中出错了,Promise.allSettled 依然会异步等待所有的返回,最终返回状态集合。
-- -------------------- ---- ------- ------------------------------- ----------- ----------- ------------ ----------------------- - --------------------- --- -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ----------- -- ----- --- - -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ----------- -- ----- --- - -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ---------- -- ----- --- - -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ---------- -- ----- --- -
在这个例子中,我们使用了 getUser4 函数,并在其中引发了错误。但是,Promise.allSettled 还是会异步等待结果并返回一个结果状态的集合。
结果中每个对象都有一个 status 属性,该属性是 settled、fulfilled、rejected 中的一个。
我们来看看返回值:
[ { "status": "fulfilled", "value": 1 }, { "status": "fulfilled", "value": 2 }, { "status": "rejected", "reason": 3 }, { "status": "rejected", "reason": 4 } ]
可以看到,每个 Promise 实例的状态都被等待到之后才执行,而且其中的 fulfilled 或者 reject 状态也以对象形式被统一返回了。
为了更形象一些,我们来看看下面的代码:
var promise1 = Promise.resolve(3); var promise2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, 'foo'); }); Promise.allSettled([promise1, promise2]) .then(function(results) { console.log(results); });
这个代码在前两个 Promise 实例中是已知的。可以看到,我们使用 Promise.resolve 和 Promise 构造函数来创建返回数字 3 和字符串 'foo' 的 Promise 实例,而结果数组始终都是相同的:
[ { "status": "fulfilled", "value": 3 }, { "status": "fulfilled", "value": "foo" } ]
Promise.allSettled 的使用
Promise.allSettled 在某些情况下比 Promise.all 更加有用。当 Promise 数组中有 Promise 实例的状态不确定时, Promise.all 会挂起并等待所有 Promise 实例都返回, 而 Promise.allSettled 不会等待所有 Promise 实例都返回,它只是等待所有 Promise 实例都准备好返回。
在下面的代码中,getUser1 和 getUser2 都返回了相应的成功信息,但是 getUser3 函数在进行异步操作的时候会引发 Error。在这种情况下,Promise.all 会被拒绝并返回一个错误的菜单,而Promise.allSettled依然会执行并返回所有值集合的状态。
-- -------------------- ---- ------- ------------------------ ----------- ------------ ----------------------- - --------------------- -- -------------------- - ----------------- --- ------------------------------- ----------- ------------ ----------------------- - --------------------- --- -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ----------- -- ----- --- - -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ----------- -- ----- --- - -------- ---------- - ------ --- ------------------------- ------- - --------------------- - ---------- ---------------- -- ----- --- -
这是包含 catch 函数的 Promise.all 和包含 then 函数的 Promise.allSettled 的输出结果:
结果 1:
[ { "status": "rejected", "reason": Error: error at ... }, undefined, undefined ]
结果 2:
[ { "status": "fulfilled", "value": 1 }, { "status": "fulfilled", "value": 2 }, { "status": "rejected", "reason": Error: error at ... } ]
在这个例子中,我们使用了 getUser3 函数并引发了错误。但是,在使用 Promise.all 时,我们只会得到一个错误,而使用 Promise.allSettled 时我们会得到三种不同的状态值集合。
总结
在本文中,我们详细学习了 JavaScript 中的 Promise 及其基本使用和优化的知识。同时,我们重点介绍了 Promise.allSettled 方法,因为对于处理异步操作时,这个方法比 Promise.all 更加实用和有意义。
要使得代码更加简洁和可读,同时所处理的异步操作序列更加优美和直观,我们可以结合 Promises 与 Promise.allSettled,在 Promise 链中嵌入 Promise.allSettled 来获得异步过程中的更多信息。
无论是 Promise.all 为多个 Promise 实例返回值的混合处理,还是 Promise.allSettled 提供了一个比 Promise.all 更细致更完整状态集合,他们恰好在不同情形下都有优越之处。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6455b9e5968c7c53b09200e1