Promise API概览

让我们复习一下我们已经在本章中零散地展开的ES6PromiseAPI。

注意: 下面的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(..)

  1. var p = new Promise( function(resolve,reject){
  2. // `resolve(..)`给解析/完成的promise
  3. // `reject(..)`给拒绝的promise
  4. } );

reject(..)简单地拒绝promise,但是resolve(..)既可以完成promise,也可以拒绝promise,这要看它被传入什么值。如果resolve(..)被传入一个立即的,非Promise,非thenable的值,那么这个promise将用这个值完成。

但如果resolve(..)被传入一个Promise或者thenable的值,那么这个值将被递归地展开,而且无论它最终解析结果/状态是什么,都将被promise采用。

Promise.resolve(..) 和 Promise.reject(..)

一个用于创建已被拒绝的Promise的简便方法是Promise.reject(..),所以这两个promise是等价的:

  1. var p1 = new Promise( function(resolve,reject){
  2. reject( "Oops" );
  3. } );
  4. var p2 = Promise.reject( "Oops" );

Promise.reject(..)相似,Promise.resolve(..)通常用来创建一个已完成的Promise。然而,Promise.resolve(..)还会展开thenale值(就像我们已经几次讨论过的)。在这种情况下,返回的Promise将会采用你传入的thenable的解析,它既可能是完成,也可能是拒绝:

  1. var fulfilledTh = {
  2. then: function(cb) { cb( 42 ); }
  3. };
  4. var rejectedTh = {
  5. then: function(cb,errCb) {
  6. errCb( "Oops" );
  7. }
  8. };
  9. var p1 = Promise.resolve( fulfilledTh );
  10. var p2 = Promise.resolve( rejectedTh );
  11. // `p1`将是一个完成的promise
  12. // `p2`将是一个拒绝的promise

而且要记住,如果你传入一个纯粹的Promise,Promise.resolve(..)不会做任何事情;它仅仅会直接返回这个值。所以在你不知道其本性的值上调用Promise.resolve(..)不会有额外的开销,如果它偶然已经是一个纯粹的Promise。

then(..) 和 catch(..)

每个Promise实例(不是 Promise API 名称空间)都有then(..)catch(..)方法,它们允许你为Promise注册成功或拒绝处理器。一旦Promise被解析,它们中的一个就会被调用,但不是都会被调用,而且它们总是会被异步地调用(参见第一章的“Jobs”)。

then(..)接收两个参数,第一个用于完成回调,第二个用户拒绝回调。如果它们其中之一被省略,或者被传入一个非函数的值,那么一个默认的回调就会分别顶替上来。默认的完成回调简单地将值向下传递,而默认的拒绝回调简单地重新抛出(传播)收到的拒绝理由。

catch(..)仅仅接收一个拒绝回调作为参数,而且会自动的顶替一个默认的成功回调,就像我们讨论过的。换句话说,它等价于then(null,..)

  1. p.then( fulfilled );
  2. p.then( fulfilled, rejected );
  3. p.catch( rejected ); // 或者`p.then( null, rejected )`

then(..)catch(..)也会创建并返回一个新的promise,它可以用来表达Promise链式流程控制。如果完成或拒绝回调有异常被抛出,这个返回的promise就会被拒绝。如果这两个回调之一返回一个立即,非Promise,非thenable值,那么这个值就会作为被返回的promise的完成。如果完成处理器指定地返回一个promise或thenable值这个值就会被展开而且变成被返回的promise的解析。

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

在ES6的PromiseAPI的静态帮助方法Promise.all([ .. ])Promise.race([ .. ])都创建一个Promise作为它们的返回值。这个promise的解析完全由你传入的promise数组控制。

对于Promise.all([ .. ]),为了被返回的promise完成,所有你传入的promise都必须完成。如果其中任意一个被拒绝,返回的主promise也会立即被拒绝(丢弃其他所有promise的结果)。至于完成状态,你会收到一个含有所有被传入的promise的完成值的array。至于拒绝状态,你仅会收到第一个promise拒绝的理由值。这种模式通常称为“门”:在门打开前所有人都必须到达。

对于Promise.race([ .. ]),只有第一个解析(成功或拒绝)的promise会“胜出”,而且不论解析的结果是什么,都会成为被返回的promise的解析结果。这种模式通常成为“闩”:第一个打开门闩的人才能进来。考虑这段代码:

  1. var p1 = Promise.resolve( 42 );
  2. var p2 = Promise.resolve( "Hello World" );
  3. var p3 = Promise.reject( "Oops" );
  4. Promise.race( [p1,p2,p3] )
  5. .then( function(msg){
  6. console.log( msg ); // 42
  7. } );
  8. Promise.all( [p1,p2,p3] )
  9. .catch( function(err){
  10. console.error( err ); // "Oops"
  11. } );
  12. Promise.all( [p1,p2] )
  13. .then( function(msgs){
  14. console.log( msgs ); // [42,"Hello World"]
  15. } );

警告: 要小心!如果一个空的array被传入Promise.all([ .. ]),它会立即完成,但Promise.race([ .. ])却会永远挂起,永远不会解析。

ES6的PromiseAPI十分简单和直接。对服务于大多数基本的异步情况来说它足够好了,而且当你要把你的代码从回调地狱变为某些更好的东西时,它是一个开始的好地方。

但是依然还有许多应用程序所要求的精巧的异步处理,由于Promise本身所受的限制而不能解决。在下一节中,为了有效利用Promise库,我们将深入检视这些限制。