用例子直观的陈列 Promise 的行为作为笔记(如果能帮助新手快速了解 Promise 的使用自然最好,最终还是希望不但要学会使用还要了解规范),此行为规范基于的是 ES6 的规范,以后 JS 规范更新可能改变某些行为。原理及规范请查看考如下资料:
- 《You Don't Know JS: Async & Performance - Chapter 3: Promises》 这是著名的《你不知道的JavaScript》的英文原版章节。-- 这篇对于新人的缺点是过长。
- Promises/A+ 规范 这篇是 ES6 Promise 规范的前身。 -- 这篇对于新人的缺点是技术点不容易看懂。
- ES6 Promise 规范 -- 最权威的规范。
- 基础(如果对 Promise 没有一点了解的,请移步 MDN Promise)
var p1 = new Promise( function(resolve, reject){
resolve( 1 )
} )
var p2 = new Promise( function(resolve, reject){
setTimeout( function(){
resolve( 2 )
}, 1000 );
} )
p1.then( function(value){
console.log( value ); // 1
}, function(){
// never gets here
} )
p2.then( function(value){
console.log( value ); // 2
}, function(){
// never gets here
} )
无论构造时候 executor 中被传入的 resolve 函数是同步地被调用还是异步地被调用,只要传入的是一个非 promise 非 thenable 的值,都会在 then 传入的第一个回调函数(onFulfilled)中获得这个值,第二个回调函数(onRejected)不会被调用到。
<br />
- 如果在 executor 中调用 reject
var p1 = new Promise( function(resolve, reject){
reject( 'rejected because ...' )
} )
p1.then( function(value){
console.log( value ); // never gets here
}, function(reason){
console.log( reason ); // rejected because ...
} )
<br />
- 如果 executor 中报错,promise 变成 rejected 状态,then 传入的第二个回调函数(onRejected)中会获得错误对象。
var p1 = new Promise( function(resolve, reject){
throw new Error( 'test error' )
resolve( 1 )
} )
var p2 = new Promise( function(resolve, reject){
'1'.toFixed() // number has not toString method ,so it will throw exception
resolve( 2 )
} )
p1.then( function(value){
console.log( value ); // never gets here
}, function(error){
console.log( error.message ) // test error
} )
p2.then( function(value){
console.log( value ); // never gets here
}, function(error){
console.log( error.message ) // "1".toFixed is not a function
} )
<br />
- then 会返回另一个 promise,这个 promise 会使用 then( onFulfilled, onRejected ) 两个回调函数中的任何一个函数的返回值作为成功状态的值。这么做的目的是可以产生链式调用。
var p1 = new Promise( function(resolve, reject){
resolve( 1 )
} )
var p11 = p1.then( function(value){
return value * 3;
}, funtion(){
// never gets here
} );
p11.then( function(value){
console.log( value ); // 3
}, function(){}{
// never gets here
} )
/*
上述改成链式调用的写法就是:
p1.then( function(value){
return value * 3;
} ).then( function(value){
console.log( value ); // 3
} )
*/
var p2 = new Promise( function(resolve, reject){
'1'.toFixed() // number has not toString method ,so it will throw exception
resolve( 2 ) // never gets here
} )
// p2 rejected的情况,以下采用链式调用写法
p2.then( function(){
// never gets here
}, function(error){
console.log( error.message ) // "1".toFixed is not a function
return 4;
} ).then( function(value){
console.log( value ); // 4
} )
// 如果 onFullfilled, onRejected 没有返回则会获得 undefined,
new Promise( function(resolve, reject){
resolve( 1 )
} ).then( function(value){
console.log( value ); // 1
}, function(){
// never gets here
} ).then( function(value){
console.log( value ); // undefined
}, function(){
// never gets here
} )
一个 promise.then 中的两个回调函数只有一个会被调用,因为 promise 的状态要么是成功的,要么是失败的,不会在成功失败间相互转换。
<br />
- then( onFulfilled, onRejected ) 两个回调函数中的任意一个函数在被执行时候抛出异常,则 then 返回的 promise 变成失败的状态,其 onRejected 被调用。
new Promise( function(resolve, reject){
resolve( 1 );
} ).then( function(){
throw new Error( 'test error1' );
return 2; // never gets here
} ).then( function(){
// never gets here
}, function(error){
console.log( error.message ); // test error1
} )
new Promise( function(resolve, reject){
throw new Error( 'test error2' );
resolve( 1 ); // never gets here
} ).then( function(){
// never gets here
}, function(error){
console.log( error.message ); // test error2
throw new Error( 'test error3' );
} ).then( function(error){
// never gets here
}, function(error){
console.log( error.message ); // test error3
} )
<br />
- 如果 promise1.then( onFulfilled, onRejected ) 没有传入回调函数,则 then 返回的 promise2 继承 promise1 的状态。从原理上来说就是 promsie1 成功或者失败后调用 then 没有函数去处理(成功没有注册 onFulfilled,失败没有注册 onRejected),则 then 返回的 promise2 将依然保持 promise1 的状态。
new Promise( function(resolve, reject){
resolve(1);
} ).then(
null,
null
).then( function(value){
console.log( value ); // 1
} )
new Promise( function(resolve, reject){
throw new Error( 'test error' );
resolve(1); // never gets here
} ).then().then( function(){
// never gets here
}, function(error){
console.log( error.message ); // test error
} )
<br />
- 上面所列的都是一般常见情况,如果在 promise1 的 executor 中 resolve 了一个 promise0 将怎么处理?promise1 将把自己的状态和 promise0 同步。
var p0 = new Promise( function(res,rej){ res(0); } ); // fulfilled promise
var p1 = new Promise( function(resolve, reject){
resolve( p0 );
} );
p1.then( function(value){
console.log( value ); // 0
}, function(){
// never gets here
} );
var p2 = new Promise( function(res,rej){ throw new Error('test error') } ); // rejected promise
new Promise( function(resolve, reject){
resolve( p2 );
} ).then( function(){
// never gets here
}, function(error){
console.log( error.message ); // test error
} )
<br />
- 如果 promise1.then( onFulfilled, onReject ) 中任意一个回调函数中 return 一个 promise0,那么 then 返回的 promsie2 也将把自己的状态和 promise0 同步。
var p0 = new Promise( function(res,rej){ res(0); } );
new Promise( function(resolve, reject){
resolve( 1 );
} ).then( function(){
return p0;
} ).then( function(value){
console.log( value ); // 0
} )
new Promise( function(resolve, reject){
throw new Error( 'make rejected' );
} ).then( null, function(error){
return p0
} ).then( function(value){
console.log( value ); // 0
} )
<br />
- 可以用 promise.catch( onRejected ) 来代替 promise.then( ..., onRejected ),这样写链式调用起来更优雅。
var p1 = new Promise( function(){
throw new Error( 'make rejected' );
} );
p1.then( null, function( error ){
console.log( error.message ); // make rjected
} );
p1.catch( function(error) {
console.log( error.message ); // make rjected
} );
// p1.then(null,onRejected) 和 p1.catch(onRejected) 是完全等价的。
// 链式调用
new Promise( function(resolve, rejected){
resolve( 0 );
} ).then( function(value){
// some code here, may generate some error
return value++;
} ).then( function(value){
// some code here, may generate some error
return value++;
} ).then( function(value){
// some code here, may generate some error
return value++;
} ).then( function(value){
// some code here, may generate some error
return value++;
} ).catch( function(error){
// 上面任何一个 then 都没有设置 onRejected 回调函数,意味着,一旦有一个 onFulfilled 里面一旦报错,则一个失败状态的 promise 会得不到处理,一直延续到最后一个被 catch 处理。
// 如果理解不了,则自行拆解每个 then
} )
-
Promise.resolve( value )
返回一个成功的 promise1,其值是 value。如果 value 是一个 promise0,意味着 promise1 将把自己的状态和 promise0 同步,ES6 进一步优化了这种情况,如果 value 是一个 promise,则直接返回这个 promise。
var p1 = Promise.resolve( 42 );
p1.then( function(value){
console.log( value ); // 42
} );
var p2 = Promise.resolve( p1 );
p2 === p1; // true
-
Promise.reject( reason )
返回一个失败的 promise1,其值是 reason。这个无二义性。
var p1 = Promise.reject( 'make rejected' );
p1.catch( function(reason){
console.log( reason ); // make rejected
} );
var p2 = Promise.reject( p1 );
p2.catch( function(reason){
console.log( reason ); // p1: Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: "make rejected"}
} );
<br />
-
Promise.all( [promise0, promise0, ..., promiseN] )
返回一个 promsie,如果 promise0 - promiseN 全部成功了,则这个最终的 promise 成功。如果但凡 promise0 - promiseN 中有任意一个失败了,则最终的 promise 立即失败。- 0 - n 都成功则 Promise.all( [promise0, promise0, ..., promiseN] ).then 的 onFulfilled 接收一个数组作为参数,数组里面的值对应的是 0 - n 个 promise 的成功值。
- 如果有一个失败了,Promise.all( [promise0, promise0, ..., promiseN] ).then 的 onRejected 就接收这个失败的 promise 的失败理由。
-
Promise.all( [1, 'abc', ..., promiseN] )
参数数组允许非 promise 的值,Promise.all 这个方法会把所有非 Promise 对象的值用 Promise.resolve( v ) 包装成一个 promise。
<br />
-
Promise.race( [promise0, promise0, ..., promiseN] )
返回一个 promsie,这个返回的 promise 状态就是 promise0 - promiseN 中第一个成功或者第一个失败的 promise 的状态。- 注意
Promise.race( [] ).then( onFulfilled, onRejected )
如果参数为空数组,则返回的 promise 永远不会成功或者失败(onFulfilled, onRejected 永远不会被调用)。因为空数组中没有 promise 会成功失败,即永远没有 promise 来竞争来使 Promise.race 返回的 promise 变成功或者失败。
- 注意
<br />
文中总结了 promise 的一般用法,没有涉及到异步和 thenable 的概念。异步的概念我还需要进一步看些资料,一时我也没有钻的比较深。thenable 除非你项目是老项目,里面会用到 promise 出现前的一些内容(譬如 jQuery 中的 defer),一般是不太会涉及到这个概念,你就先理解成 thenable 对象是一个带 then 方法的对象,但是它本身不是 Promise 对象,具体还是希望各位看官查看文章开始所列的资料。