返回介绍

第一部分 类型和语法

第二部分 异步和性能

3.7 Promise API 概述

发布于 2023-05-24 16:38:21 字数 3947 浏览 0 评论 0 收藏 0

本章已经在多处零零碎碎地展示了 ES6 Promise API,现在让我们来总结一下。

下面的 API 只对于 ES6 是原生的,但是有符合规范的适配版(不只是对 Promise 库的扩展),其定义了 Promise 及它的所有相关特性,这样你在前 ES6 浏览器中也可以使用原生 Promise。这样的适配版之一是 Native Promise Only(http://github.com/getify/native-promise-only ),是我写的。

3.7.1 new Promise(..) 构造器

有启示性的构造器 Promise(..) 必须和 new 一起使用,并且必须提供一个函数回调。这个回调是同步的或立即调用的。这个函数接受两个函数回调,用以支持 promise 的决议。通常我们把这两个函数称为 resolve(..) 和 reject(..) :

var p = new Promise( function(resolve,reject){
  // resolve(..)用于决议/完成这个promise
  // reject(..)用于拒绝这个promise
} );

reject(..) 就是拒绝这个 promise;但 resolve(..) 既可能完成 promise,也可能拒绝,要根据传入参数而定。如果传给 resolve(..) 的是一个非 Promise、非 thenable 的立即值,这个 promise 就会用这个值完成。

但是,如果传给 resolve(..) 的是一个真正的 Promise 或 thenable 值,这个值就会被递归展开,并且(要构造的)promise 将取用其最终决议值或状态。

3.7.2 Promise.resolve(..)Promise.reject(..)

创建一个已被拒绝的 Promise 的快捷方式是使用 Promise.reject(..) ,所以以下两个 promise 是等价的:

var p1 = new Promise( function(resolve,reject){
  reject( "Oops" );
} );

var p2 = Promise.reject( "Oops" );

Promise.resolve(..) 常用于创建一个已完成的 Promise,使用方式与 Promise.reject(..) 类似。但是,Promise.resolve(..) 也会展开 thenable 值(前面已多次介绍)。在这种情况下,返回的 Promise 采用传入的这个 thenable 的最终决议值,可能是完成,也可能是拒绝:

var fulfilledTh = {
  then: function(cb) { cb( 42 ); }
};
var rejectedTh = {
  then: function(cb,errCb) {
    errCb( "Oops" );
  }
};

var p1 = Promise.resolve( fulfilledTh );
var p2 = Promise.resolve( rejectedTh );

// p1是完成的promise
// p2是拒绝的promise

还要记住,如果传入的是真正的 Promise,Promise.resolve(..) 什么都不会做,只会直接把这个值返回。所以,对你不了解属性的值调用 Promise.resolve(..) ,如果它恰好是一个真正的 Promise,是不会有额外的开销的。

3.7.3 then(..)catch(..)

每个 Promise 实例(不是 Promise API 命名空间)都有 then(..) 和 catch(..) 方法,通过这两个方法可以为这个 Promise 注册完成和拒绝处理函数。Promise 决议之后,立即会调用这两个处理函数之一,但不会两个都调用,而且总是异步调用(参见 1.5 节)。

then(..) 接受一个或两个参数:第一个用于完成回调,第二个用于拒绝回调。如果两者中的任何一个被省略或者作为非函数值传入的话,就会替换为相应的默认回调。默认完成回调只是把消息传递下去,而默认拒绝回调则只是重新抛出(传播)其接收到的出错原因。

就像刚刚讨论过的一样,catch(..) 只接受一个拒绝回调作为参数,并自动替换默认完成回调。换句话说,它等价于 then(null,..) :

p.then( fulfilled );

p.then( fulfilled, rejected );

p.catch( rejected ); // 或者p.then( null, rejected )

then(..) 和 catch(..) 也会创建并返回一个新的 promise,这个 promise 可以用于实现 Promise 链式流程控制。如果完成或拒绝回调中抛出异常,返回的 promise 是被拒绝的。如果任意一个回调返回非 Promise、非 thenable 的立即值,这个值会被用作返回 promise 的完成值。如果完成处理函数返回一个 promise 或 thenable,那么这个值会被展开,并作为返回 promise 的决议值。

3.7.4 Promise.all([ .. ])Promise.race([ .. ])

ES6 Promise API 静态辅助函数 Promise.all([ .. ]) 和 Promise.race([ .. ]) 都会创建一个 Promise 作为它们的返回值。这个 promise 的决议完全由传入的 promise 数组控制。

对 Promise.all([ .. ]) 来说,只有传入的所有 promise 都完成,返回 promise 才能完成。如果有任何 promise 被拒绝,返回的主 promise 就立即会被拒绝(抛弃任何其他 promise 的结果)。如果完成的话,你会得到一个数组,其中包含传入的所有 promise 的完成值。对于拒绝的情况,你只会得到第一个拒绝 promise 的拒绝理由值。这种模式传统上被称为门:所有人都到齐了才开门。

对 Promise.race([ .. ]) 来说,只有第一个决议的 promise(完成或拒绝)取胜,并且其决议结果成为返回 promise 的决议。这种模式传统上称为门闩:第一个到达者打开门闩通过。考虑:

var p1 = Promise.resolve( 42 );
var p2 = Promise.resolve( "Hello World" );
var p3 = Promise.reject( "Oops" );

Promise.race( [p1,p2,p3] )
.then( function(msg){
  console.log( msg );   // 42
} );

Promise.all( [p1,p2,p3] )
.catch( function(err){
  console.error( err );   // "Oops"

} );

Promise.all( [p1,p2] )
.then( function(msgs){
  console.log( msgs );  // [42,"Hello World"]
} );

当心!若向 Promise.all([ .. ]) 传入空数组,它会立即完成,但 Promise.race([ .. ]) 会挂住,且永远不会决议。

ES6 Promise API 非常简单直观。它至少足以处理最基本的异步情况,并且如果要重新整理,把代码从回调地狱解救出来的话,它也是一个很好的起点。

但是,应用常常会有很多更复杂的异步情况需要实现,而 Promise 本身对此在处理上具有局限性。下一节会深入探讨这些局限,理解 Promise 库出现的动机。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文