不深入只浅出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)不会被调用到。

<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 对象,具体还是希望各位看官查看文章开始所列的资料。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容