在 Node.js 中使用 Promise.prototype.finally

Promise.prototype.finally()  最近达到了 TC39 提案的 第 4 阶段 。这意味着 Promise.prototype.finally() 提案被采纳成为 ECMAScript 最新特性草案 的一部分,登陆 Node.js 现在只是时间问题了。这篇文章会向大家展示 Promise.prototype.finally() 的用法和简化版 Polyfill 的写法。

Promise.prototype.finally() 是什么?

假设你创建了一个新的 Promise:

你可以用 .then() 函数把这些 Promise 串联在一起。

注意 .then() 需要两个函数作为参数。第一个参数是 onFulfilled(),当 Promise 为 fulfilled 时调用;第二个 onRejected() 则是在 rejected 的时候调用。Promise 是一个必定处于以下三种状态之一的状态机:

  • pending(进行中): Promise 中的操作正在进行中,状态未被凝固为 fulfilled 或 rejected。
  • fulfilled(已完成,直译:已满足): Promise 中的操作已成功完成,现在 Promise 里面关联有该操作的返回值。
  • rejected(已失败,直译:已回绝): Promise 中的操作因某些原因失败,现在 Promise 里面关联有该操作的错误信息。

此外,处于 fulfilled 或者 rejected 状态的 Promise 称作“已凝固”(settled) 的 Promise。

虽然 .then() 是串联 Promise 的核心机制,但并不独一无二。Promise 用来处理抛出错误的 .catch() 函数 也能串联 Promise。

.catch() 函数只是一个只有 onRejected() 参数的 .then() 的语法糖:

类似于 .catch(),.finally() 也是 .then() 的一个语法糖。区别在于 .finally() 当 Promise 凝固(fulfilled / rejected)时执行一个 onFinally 函数。当前 .finally() 还没有加入 Node.js 发行版,但 npm 上的 promise.prototype.finally 模块 实现了它的 Polyfill。

上面代码的运行结果会打印 ‘fulfilled’ 和 ‘rejected’,因为无论是 fulfilled 还是 rejected,只要状态凝固 onFinally 都会立即执行。不过 onFinally 接受参数,所以你无法判断 Promise 的状态到底是两个中的哪个。

finally() 会返回一个 Promise,所以你可以使用 .then() / .catch() / .finally() 串联它的返回值。finally() 返回的 Promise 会和它连接到的 Promise 保持相同的 fulfill 条件。 例如下面的代码,即使 onFinally 返回了 ‘bar’,它还是会打印 5 次 ‘foo’ 。

类似地,下面代码中即使 onFinally 没有抛出任何错误,仍然会打印 ‘foo’。

上面代码展示了使用 finally() 的一个重要细节:它 不会 帮你处理 Promise 的错误。如何让它能处理 Promise 错误值得更深入的研究。

错误处理

finally() 不是 用来处理 Promise 的错误的。事实上,它会在 onFinally() 执行后显式重新抛错。下面的代码会打印一个未被处理的 Promise 错误警告。

与 try/catch/finally 类似,通常 .finally() 都会在 .catch() 后面被调用。

然而 finally() 返回的也是 Promise,所以你可以随意在 finally() 后面调用 .catch()。特别地,如果 onFinally 会出错,例如 HTTP 请求,你应该在末尾添加 .catch() 以处理可能发生的错误。

简版 Polyfill

我觉得想要真正搞懂一个东西,最简单的方式就是自己去实现一个。.finally() 是一个很好的选择,因为官方 Polyfill 只有 45 行,而且大多数代码在验证原理时可以进一步精简。

接下来是一些关于 .finally() 的测试样例。下面的代码会打印 ‘foo’ 5 次。

下面是简版 Polyfill 的实现。

这个实现背后关键的思路在于 onFinally 可能返回 Promise。在这种情况下你需要用 .then() 来处理它并且给外层 Promise 凝固状态。你可以显式检查 onFinally 是否返回 Promise,但 Promise.resolve() 已经帮你做了,而且不需要 if 语句。你还需要跟踪初始 Promise 的值或错误,并确保 finally() 返回的 Promise 解析出初始值 res,或重新抛出初始错误 err。

后记

在动笔时,Promise.prototype.finally() 是 8 个 TC39 第四阶段提案 之一。这意味着 finally() 将和 7 个其他新语言特性一起加入 Node.js。 finally() 是这 8 个新特性中最令人兴奋的之一,皆因为它可以让异步操作结束后的清理更彻底。举个例子,下面我正用在生产环境的代码非常需要 finally() 来在函数完成时释放资源的锁定。

1 1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部