3.7 Promise API 概述
本章已经在多处零零碎碎地展示了 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论