Promise API概览
让我们复习一下我们已经在本章中零散地展开的ES6Promise
API。
注意: 下面的API尽管在ES6中是原生的,但也存在一些语言规范兼容的填补(不光是扩展Promise库),它们定义了Promise
和与之相关的所有行为,所以即使是在前ES6时代的浏览器中你也以使用原生的Promise。这类填补的其中之一是“Native Promise Only”(http://github.com/getify/native-promise-only),我写的!
new Promise(..)构造器
揭示构造器(revealing constructor) Promise(..)
必须与new
一起使用,而且必须提供一个被同步/立即调用的回调函数。这个函数被传入两个回调函数,它们作为promise的解析能力。我们通常将它们标识为resolve(..)
和reject(..)
:
var p = new Promise( function(resolve,reject){
// `resolve(..)`给解析/完成的promise
// `reject(..)`给拒绝的promise
} );
reject(..)
简单地拒绝promise,但是resolve(..)
既可以完成promise,也可以拒绝promise,这要看它被传入什么值。如果resolve(..)
被传入一个立即的,非Promise,非thenable的值,那么这个promise将用这个值完成。
但如果resolve(..)
被传入一个Promise或者thenable的值,那么这个值将被递归地展开,而且无论它最终解析结果/状态是什么,都将被promise采用。
Promise.resolve(..) 和 Promise.reject(..)
一个用于创建已被拒绝的Promise的简便方法是Promise.reject(..)
,所以这两个promise是等价的:
var p1 = new Promise( function(resolve,reject){
reject( "Oops" );
} );
var p2 = Promise.reject( "Oops" );
与Promise.reject(..)
相似,Promise.resolve(..)
通常用来创建一个已完成的Promise。然而,Promise.resolve(..)
还会展开thenale值(就像我们已经几次讨论过的)。在这种情况下,返回的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。
then(..) 和 catch(..)
每个Promise实例(不是 Promise
API 名称空间)都有then(..)
和catch(..)
方法,它们允许你为Promise注册成功或拒绝处理器。一旦Promise被解析,它们中的一个就会被调用,但不是都会被调用,而且它们总是会被异步地调用(参见第一章的“Jobs”)。
then(..)
接收两个参数,第一个用于完成回调,第二个用户拒绝回调。如果它们其中之一被省略,或者被传入一个非函数的值,那么一个默认的回调就会分别顶替上来。默认的完成回调简单地将值向下传递,而默认的拒绝回调简单地重新抛出(传播)收到的拒绝理由。
catch(..)
仅仅接收一个拒绝回调作为参数,而且会自动的顶替一个默认的成功回调,就像我们讨论过的。换句话说,它等价于then(null,..)
:
p.then( fulfilled );
p.then( fulfilled, rejected );
p.catch( rejected ); // 或者`p.then( null, rejected )`
then(..)
和catch(..)
也会创建并返回一个新的promise,它可以用来表达Promise链式流程控制。如果完成或拒绝回调有异常被抛出,这个返回的promise就会被拒绝。如果这两个回调之一返回一个立即,非Promise,非thenable值,那么这个值就会作为被返回的promise的完成。如果完成处理器指定地返回一个promise或thenable值这个值就会被展开而且变成被返回的promise的解析。
Promise.all([ .. ]) 和 Promise.race([ .. ])
在ES6的Promise
API的静态帮助方法Promise.all([ .. ])
和Promise.race([ .. ])
都创建一个Promise作为它们的返回值。这个promise的解析完全由你传入的promise数组控制。
对于Promise.all([ .. ])
,为了被返回的promise完成,所有你传入的promise都必须完成。如果其中任意一个被拒绝,返回的主promise也会立即被拒绝(丢弃其他所有promise的结果)。至于完成状态,你会收到一个含有所有被传入的promise的完成值的array
。至于拒绝状态,你仅会收到第一个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"]
} );
警告: 要小心!如果一个空的array
被传入Promise.all([ .. ])
,它会立即完成,但Promise.race([ .. ])
却会永远挂起,永远不会解析。
ES6的Promise
API十分简单和直接。对服务于大多数基本的异步情况来说它足够好了,而且当你要把你的代码从回调地狱变为某些更好的东西时,它是一个开始的好地方。
但是依然还有许多应用程序所要求的精巧的异步处理,由于Promise本身所受的限制而不能解决。在下一节中,为了有效利用Promise库,我们将深入检视这些限制。