Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

不深入只浅出ES6 Promise | 笔记 #22

Open
sixwinds opened this issue Apr 2, 2017 · 0 comments
Open

不深入只浅出ES6 Promise | 笔记 #22

sixwinds opened this issue Apr 2, 2017 · 0 comments

Comments

@sixwinds
Copy link
Owner

sixwinds commented Apr 2, 2017

不深入只浅出ES6 Promise | 笔记

用例子直观的陈列 Promise 的行为作为笔记(如果能帮助新手快速了解 Promise 的使用自然最好,最终还是希望不但要学会使用还要了解规范),此行为规范基于的是 ES6 的规范,以后 JS 规范更新可能改变某些行为。原理及规范请查看考如下资料:

  1. 《You Don't Know JS: Async & Performance - Chapter 3: Promises》 这是著名的《你不知道的JavaScript》的英文原版章节。-- 这篇对于新人的缺点是过长。
  2. Promises/A+ 规范 这篇是 ES6 Promise 规范的前身。 -- 这篇对于新人的缺点是技术点不容易看懂。
  3. 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)不会被调用到。

  • 如果在 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 ...
} )
  • 如果 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
} )
  • 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 的状态要么是成功的,要么是失败的,不会在成功失败间相互转换。

  • 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
} )
  • 如果 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
} )
  • 上面所列的都是一般常见情况,如果在 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
} )
  • 如果 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
} )
  • 可以用 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"}
} );
  • Promise.all( [promise0, promise0, ..., promiseN] ) 返回一个 promsie,如果 promise0 - promiseN 全部成功了,则这个最终的 promise 成功。如果但凡 promise0 - promiseN 中有任意一个失败了,则最终的 promise 立即失败。

    1. 0 - n 都成功则 Promise.all( [promise0, promise0, ..., promiseN] ).then 的 onFulfilled 接收一个数组作为参数,数组里面的值对应的是 0 - n 个 promise 的成功值。
    2. 如果有一个失败了,Promise.all( [promise0, promise0, ..., promiseN] ).then 的 onRejected 就接收这个失败的 promise 的失败理由。
    3. Promise.all( [1, 'abc', ..., promiseN] ) 参数数组允许非 promise 的值,Promise.all 这个方法会把所有非 Promise 对象的值用 Promise.resolve( v ) 包装成一个 promise。
  • Promise.race( [promise0, promise0, ..., promiseN] ) 返回一个 promsie,这个返回的 promise 状态就是 promise0 - promiseN 中第一个成功或者第一个失败的 promise 的状态。

    1. 注意 Promise.race( [] ).then( onFulfilled, onRejected ) 如果参数为空数组,则返回的 promise 永远不会成功或者失败(onFulfilled, onRejected 永远不会被调用)。因为空数组中没有 promise 会成功失败,即永远没有 promise 来竞争来使 Promise.race 返回的 promise 变成功或者失败。

文中总结了 promise 的一般用法,没有涉及到异步和 thenable 的概念。异步的概念我还需要进一步看些资料,一时我也没有钻的比较深。thenable 除非你项目是老项目,里面会用到 promise 出现前的一些内容(譬如 jQuery 中的 defer),一般是不太会涉及到这个概念,你就先理解成 thenable 对象是一个带 then 方法的对象,但是它本身不是 Promise 对象,具体还是希望各位看官查看文章开始所列的资料。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant