美文网首页前端JS
不深入只浅出ES6 Promise | 笔记

不深入只浅出ES6 Promise | 笔记

作者: 唯泥Bernie | 来源:发表于2017-03-15 17:55 被阅读190次

用例子直观的陈列 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)不会被调用到。

<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 立即失败。
    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。

<br />

  • Promise.race( [promise0, promise0, ..., promiseN] ) 返回一个 promsie,这个返回的 promise 状态就是 promise0 - promiseN 中第一个成功或者第一个失败的 promise 的状态。
    1. 注意 Promise.race( [] ).then( onFulfilled, onRejected ) 如果参数为空数组,则返回的 promise 永远不会成功或者失败(onFulfilled, onRejected 永远不会被调用)。因为空数组中没有 promise 会成功失败,即永远没有 promise 来竞争来使 Promise.race 返回的 promise 变成功或者失败。

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

相关文章

网友评论

    本文标题:不深入只浅出ES6 Promise | 笔记

    本文链接:https://www.haomeiwen.com/subject/fstrnttx.html