[轉]八段代碼徹底掌握 Promise

原文地址

1.Promise的立即執(zhí)行性

var p = new Promise(function(resolve, reject){
  console.log("create a promise");
  resolve("success");
});

console.log("after new Promise");

p.then(function(value){
  console.log(value);
});

控制臺輸出:

"create a promise"
"after new Promise"
"success"

Promise對象表示未來某個將要發(fā)生的事件绢慢,但在創(chuàng)建(new)Promise時廊勃,作為Promise參數(shù)傳入的函數(shù)是會被立即執(zhí)行的纳决,只是其中執(zhí)行的代碼可以是異步代碼猬腰。有些同學會認為际起,當Promise對象調用then方法時滓鸠,Promise接收的函數(shù)才會執(zhí)行雁乡,這是錯誤的。因此糜俗,代碼中"create a promise"先于"after new Promise"輸出踱稍。

2.Promise 三種狀態(tài)

var p1 = new Promise(function(resolve,reject){
  resolve(1);
});
var p2 = new Promise(function(resolve,reject){
  setTimeout(function(){
    resolve(2);  
  }, 500);      
});
var p3 = new Promise(function(resolve,reject){
  setTimeout(function(){
    reject(3);  
  }, 500);      
});

console.log(p1);
console.log(p2);
console.log(p3);
setTimeout(function(){
  console.log(p2);
}, 1000);
setTimeout(function(){
  console.log(p3);
}, 1000);

p1.then(function(value){
  console.log(value);
});
p2.then(function(value){
  console.log(value);
});
p3.catch(function(err){
  console.log(err);
});

控制臺輸出:

Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
1
2
3
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: 3}

Promise的內(nèi)部實現(xiàn)是一個狀態(tài)機。Promise有三種狀態(tài):pending悠抹,resolved寞射,rejected。當Promise剛創(chuàng)建完成時锌钮,處于pending狀態(tài)桥温;當Promise中的函數(shù)參數(shù)執(zhí)行了resolve后,Promise由pending狀態(tài)變成resolved狀態(tài)梁丘;如果在Promise的函數(shù)參數(shù)中執(zhí)行的不是resolve方法侵浸,而是reject方法,那么Promise會由pending狀態(tài)變成rejected狀態(tài)氛谜。

p2掏觉、p3剛創(chuàng)建完成時,控制臺輸出的這兩臺Promise都處于pending狀態(tài)值漫,但為什么p1是resolved狀態(tài)呢澳腹? 這是因為p1 的函數(shù)參數(shù)中執(zhí)行的是一段同步代碼,Promise剛創(chuàng)建完成,resolve方法就已經(jīng)被調用了酱塔,因而緊跟著的輸出顯示p1是resolved狀態(tài)沥邻。我們通過兩個setTimeout函數(shù),延遲1s后再次輸出p2羊娃、p3的狀態(tài)唐全,此時p2、p3已經(jīng)執(zhí)行完成蕊玷,狀態(tài)分別變成resolved和rejected邮利。

3.Promise 狀態(tài)的不可逆性

var p1 = new Promise(function(resolve, reject){
  resolve("success1");
  resolve("success2");
});

var p2 = new Promise(function(resolve, reject){
  resolve("success");
  reject("reject");
});

p1.then(function(value){
  console.log(value);
});

p2.then(function(value){
  console.log(value);
});

控制臺輸出:

"success1"
"success"

Promise狀態(tài)的一旦變成resolved或rejected時,Promise的狀態(tài)和值就固定下來了垃帅,不論你后續(xù)再怎么調用resolve或reject方法延届,都不能改變它的狀態(tài)和值。因此贸诚,p1中resolve("success2")并不能將p1的值更改為success2祷愉,p2中reject("reject")也不能將p2的狀態(tài)由resolved改變?yōu)閞ejected.

4.鏈式調用

var p = new Promise(function(resolve, reject){
  resolve(1);
});
p.then(function(value){               //第一個then
  console.log(value);
  return value*2;
}).then(function(value){              //第二個then
  console.log(value);
}).then(function(value){              //第三個then
  console.log(value);
  return Promise.resolve('resolve'); 
}).then(function(value){              //第四個then
  console.log(value);
  return Promise.reject('reject');
}).then(function(value){              //第五個then
  console.log('resolve: '+ value);
}, function(err){
  console.log('reject: ' + err);
})

控制臺輸出:

1
2
undefined
"resolve"
"reject: reject"

Promise對象的then方法返回一個新的Promise對象,因此可以通過鏈式調用then方法赦颇。then方法接收兩個函數(shù)作為參數(shù),第一個參數(shù)是Promise執(zhí)行成功時的回調赴涵,第二個參數(shù)是Promise執(zhí)行失敗時的回調媒怯。兩個函數(shù)只會有一個被調用,函數(shù)的返回值將被用作創(chuàng)建then返回的Promise對象髓窜。這兩個參數(shù)的返回值可以是以下三種情況中的一種:

return 一個同步的值 扇苞,或者 undefined(當沒有返回一個有效值時,默認返回undefined)寄纵,then方法將返回一個resolved狀態(tài)的Promise對象鳖敷,Promise對象的值就是這個返回值。
return 另一個 Promise程拭,then方法將根據(jù)這個Promise的狀態(tài)和值創(chuàng)建一個新的Promise對象返回定踱。
throw 一個同步異常,then方法將返回一個rejected狀態(tài)的Promise, 值是該異常恃鞋。
根據(jù)以上分析崖媚,代碼中第一個then會返回一個值為2(1*2),狀態(tài)為resolved的Promise對象恤浪,于是第二個then輸出的值是2畅哑。第二個then中沒有返回值,因此將返回默認的undefined水由,于是在第三個then中輸出undefined荠呐。第三個then和第四個then中分別返回一個狀態(tài)是resolved的Promise和一個狀態(tài)是rejected的Promise,依次由第四個then中成功的回調函數(shù)和第五個then中失敗的回調函數(shù)處理。

5.Promise then() 回調異步性

var p = new Promise(function(resolve, reject){
  resolve("success");
});

p.then(function(value){
  console.log(value);
});

console.log("which one is called first ?");

控制臺輸出:

"which one is called first ?"
"success"

Promise接收的函數(shù)參數(shù)是同步執(zhí)行的泥张,但then方法中的回調函數(shù)執(zhí)行則是異步的呵恢,因此,"success"會在后面輸出圾结。

6.Promise 中的異常

var p1 = new Promise( function(resolve,reject){
  foo.bar();
  resolve( 1 );      
});

p1.then(
  function(value){
    console.log('p1 then value: ' + value);
  },
  function(err){
    console.log('p1 then err: ' + err);
  }
).then(
  function(value){
    console.log('p1 then then value: '+value);
  },
  function(err){
    console.log('p1 then then err: ' + err);
  }
);

var p2 = new Promise(function(resolve,reject){
  resolve( 2 );    
});

p2.then(
  function(value){
    console.log('p2 then value: ' + value);
    foo.bar();
  }, 
  function(err){
    console.log('p2 then err: ' + err);
  }
).then(
  function(value){
    console.log('p2 then then value: ' + value);
  },
  function(err){
    console.log('p2 then then err: ' + err);
    return 1;
  }
).then(
  function(value){
    console.log('p2 then then then value: ' + value);
  },
  function(err){
    console.log('p2 then then then err: ' + err);
  }
);

控制臺輸出:

p1 then err: ReferenceError: foo is not defined
p2 then value: 2
p1 then then value: undefined
p2 then then err: ReferenceError: foo is not defined
p2 then then then value: 1

Promise中的異常由then參數(shù)中第二個回調函數(shù)(Promise執(zhí)行失敗的回調)處理瑰剃,異常信息將作為Promise的值。異常一旦得到處理筝野,then返回的后續(xù)Promise對象將恢復正常晌姚,并會被Promise執(zhí)行成功的回調函數(shù)處理。另外歇竟,需要注意p1挥唠、p2 多級then的回調函數(shù)是交替執(zhí)行的 ,這正是由Promise then回調的異步性決定的焕议。

7.Promise.resolve()

var p1 = Promise.resolve( 1 );
var p2 = Promise.resolve( p1 );
var p3 = new Promise(function(resolve, reject){
  resolve(1);
});
var p4 = new Promise(function(resolve, reject){
  resolve(p1);
});
console.log(p1 === p2); 
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);
p4.then(function(value){
  console.log('p4=' + value);
});
p2.then(function(value){
  console.log('p2=' + value);
})
p1.then(function(value){
  console.log('p1=' + value);
})

控制臺輸出:

true
false
false
false
p2=1
p1=1
p4=1

Promise.resolve(...)可以接收一個值或者是一個Promise對象作為參數(shù)宝磨。當參數(shù)是普通值時,它返回一個resolved狀態(tài)的Promise對象盅安,對象的值就是這個參數(shù)唤锉;當參數(shù)是一個Promise對象時,它直接返回這個Promise參數(shù)别瞭。因此窿祥,p1 === p2。但通過new的方式創(chuàng)建的Promise對象都是一個新的對象蝙寨,因此后面的三個比較結果都是false晒衩。另外,為什么p4的then最先調用墙歪,但在控制臺上是最后輸出結果的呢听系?因為p4的resolve中接收的參數(shù)是一個Promise對象p1,resolve會對p1”拆箱“虹菲,獲取p1的狀態(tài)和值靠胜,但這個過程是異步的,可參考下一節(jié)毕源。

8.resolve vs reject

var p1 = new Promise(function(resolve, reject){
  resolve(Promise.resolve('resolve'));
});

var p2 = new Promise(function(resolve, reject){
  resolve(Promise.reject('reject'));
});

var p3 = new Promise(function(resolve, reject){
  reject(Promise.resolve('resolve'));
});

p1.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

p2.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

p3.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

控制臺輸出:

p3 rejected: [object Promise]
p1 fulfilled: resolve
p2 rejected: reject

Promise回調函數(shù)中的第一個參數(shù)resolve髓帽,會對Promise執(zhí)行"拆箱"動作。即當resolve的參數(shù)是一個Promise對象時脑豹,resolve會"拆箱"獲取這個Promise對象的狀態(tài)和值郑藏,但這個過程是異步的。p1"拆箱"后瘩欺,獲取到Promise對象的狀態(tài)是resolved必盖,因此fulfilled回調被執(zhí)行拌牲;p2"拆箱"后,獲取到Promise對象的狀態(tài)是rejected歌粥,因此rejected回調被執(zhí)行塌忽。但Promise回調函數(shù)中的第二個參數(shù)reject不具備”拆箱“的能力,reject的參數(shù)會直接傳遞給then方法中的rejected回調失驶。因此土居,即使p3 reject接收了一個resolved狀態(tài)的Promise,then方法中被調用的依然是rejected嬉探,并且參數(shù)就是reject接收到的Promise對象擦耀。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涩堤,隨后出現(xiàn)的幾起案子眷蜓,更是在濱河造成了極大的恐慌,老刑警劉巖胎围,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吁系,死亡現(xiàn)場離奇詭異,居然都是意外死亡白魂,警方通過查閱死者的電腦和手機汽纤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來福荸,“玉大人蕴坪,你說我怎么就攤上這事〕炎耍” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵捆等,是天一觀的道長滞造。 經(jīng)常有香客問我,道長栋烤,這世上最難降的妖魔是什么谒养? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮明郭,結果婚禮上买窟,老公的妹妹穿的比我還像新娘。我一直安慰自己薯定,他們只是感情好始绍,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著话侄,像睡著了一般亏推。 火紅的嫁衣襯著肌膚如雪学赛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天吞杭,我揣著相機與錄音盏浇,去河邊找鬼。 笑死芽狗,一個胖子當著我的面吹牛绢掰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播童擎,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼滴劲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了柔昼?” 一聲冷哼從身側響起哑芹,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捕透,沒想到半個月后聪姿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡乙嘀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年末购,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虎谢。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡盟榴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婴噩,到底是詐尸還是另有隱情擎场,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布几莽,位于F島的核電站迅办,受9級特大地震影響,放射性物質發(fā)生泄漏章蚣。R本人自食惡果不足惜站欺,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纤垂。 院中可真熱鬧矾策,春花似錦、人聲如沸峭沦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吼鱼。三九已至榄鉴,卻和暖如春履磨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背庆尘。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工剃诅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驶忌。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓矛辕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親付魔。 傳聞我的和親對象是個殘疾皇子聊品,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 00翻屈、前言Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調函數(shù)和事件——更合理和更強大妻坝。它由社區(qū)...
    夜幕小草閱讀 2,127評論 0 12
  • Promiese 簡單說就是一個容器伸眶,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果,語法上說刽宪,Pr...
    雨飛飛雨閱讀 3,348評論 0 19
  • Promise的含義: ??Promise是異步編程的一種解決方案厘贼,比傳統(tǒng)的解決方案——回調函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,164評論 0 16
  • 一看到到這個名字就已經(jīng)深深的被吸引了,開頭的這段就已經(jīng)讓我相信這個法則它的存在圣拄!每個人都有一個感覺嘴秸,真實存在的感覺...
    FAB優(yōu)優(yōu)閱讀 264評論 0 0
  • 他們終于在一起了,他們相識20年庇谆,相戀4年岳掐,他們在婚訊中有著這樣一句話“我們決定互相糾纏一輩子”。他們是舒淇饭耳、馮德...
    不鬧海就鬧心的哪吒閱讀 4,559評論 46 57