關(guān)于Promise

一、為何會有Promise懂盐?

在JavaScript的世界中褥赊,所有代碼都是單線程執(zhí)行的。由于這個“缺陷”莉恼,導(dǎo)致JavaScript的所有網(wǎng)絡(luò)操作拌喉,瀏覽器事件翼岁,都必須是異步執(zhí)行。在Promise出現(xiàn)之前是通過回調(diào)函數(shù)(callback)實現(xiàn)異步, 簡單說回調(diào)函數(shù)就是將一個方法func2作為參數(shù)傳入另一個方法func1中司光,當(dāng)func1執(zhí)行到某一步或者滿足某種條件的時候才執(zhí)行傳入的參數(shù)func2琅坡。

一般來說我們會碰到的回調(diào)嵌套都不會很多,一般就一到兩級残家,但是某些情況下榆俺,回調(diào)嵌套很多時,代碼就會非常繁瑣坞淮,邏輯已經(jīng)很難理清楚了茴晋,這種情況俗稱——回調(diào)地獄。

多層嵌套的回調(diào)中回窘,有同步/異步的方法诺擅,那么執(zhí)行順序會變得混亂,而Promise采用鏈?zhǔn)秸{(diào)用啡直,使我們的代碼更容易理解和維護(hù)烁涌,而且Promise還增加了許多有用的特性,讓我們處理異步編程得心應(yīng)手酒觅。如下面這個例子:

//回調(diào)函數(shù)寫法:
step1(function (value1) { 
  step2(value1, function(value2) { 
    step3(value2, function(value3) { 
      step4(value3, function(value4) { 
        // ...
        });
     }); 
  });
});

//Promise的寫法:
(new Promise(step1))
  .then(step2)
  .then(step3)
  .then(step4);

二撮执、什么是Promise?

  • Promise由社區(qū)最早提出和實現(xiàn),ES6將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了語法呢铆,原生提供了Promise。
  • Promise是抽象異步處理對象以及對其進(jìn)行各種操作的組件谋币。Promise就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果症概。
  • Promise就如它的意思“承諾”一樣蕾额,既然執(zhí)行了,就不管程序運行結(jié)果成功還是失敗穴豫,最終都會給一個答復(fù)凡简,而這個答復(fù)就是一個Promise對象。執(zhí)行流程如下圖:

可能上面的闡述還是不夠直觀易懂精肃,那么可參考知乎上的用“定外賣”來舉的這個例子:十五行代碼帶你搞懂Promise

Promise對象有三種狀態(tài):
1、異步操作"未完成"(pending)
2帜乞、異步操作"已完成" (resolved)
3司抱、異步操作"失敗" (rejected)

這三種狀態(tài)的變化途徑:
1、異步操作從"未完成"到"已完成"
2黎烈、異步操作從"未完成“到"失敗"

Promise對象的最終結(jié)果:
1习柠、異步操作成功 Promise對象傳回一個值匀谣,狀態(tài)變?yōu)閞esolved
2、異步操作失敗 Promise對象拋出一個錯誤资溃,狀態(tài)變?yōu)閞ejected
注意:有且只有這2種結(jié)果武翎,并且狀態(tài)一旦改變,就無法再次改變狀態(tài)溶锭,這也是它名字“承諾”的由來宝恶。

三、基本用法

1趴捅、先聲明一個Promise對象垫毙,生成Promise實例。

let p = new Promise((resolve, reject) => {
  // ...

  if (/* 異步操作成功 */){
    resolve(value);//成功拋出value
  } else {
    reject(error);//失敗拋出錯誤
  }
});

2拱绑、Promise實例生成以后综芥,可以用then方法分別指定resolved狀態(tài)和reject狀態(tài)的回調(diào)函數(shù)。

p.then((value) => {
  // 成功執(zhí)行
}, (error) => {
  // 失敗執(zhí)行   --->可選
});

注意:實例化的Promise對象會立即執(zhí)行

現(xiàn)在我們可以開始寫一個簡單的例子猎拨,比如隨機(jī)生成一個數(shù)膀藐,1秒后判斷可不可以做除數(shù)(0不能做除數(shù)),代碼如下:

let p = new Promise((resolve, reject) => {
    var divisor = Math.random();
    setTimeout(() => {
        if(divisor) {
            resolve('可以做除數(shù)~');
        }
        else {
            reject('不可以做除數(shù)~');
        }
    }, 1000);
});

console.log(p); // 在瀏覽器的控制臺運行的話红省,它返回的是一個包含了許多屬性的Promise對象

p.then((result) => {
    console.log(result);
}, (err) => {
    console.log(err);
}); // 1s后這里的輸出可能是fail也可能是success

四消请、常見方法

1、Promise.then()

promise.then(
    () => { console.log('this is success callback') },
    () => { console.log('this is fail callback') }
)

.then()方法是Promise原型鏈上的方法类腮,它包含兩個參數(shù)方法臊泰,分別是已成功resolved的回調(diào)和已失敗rejected的回調(diào)。

2蚜枢、Promise.catch()

promise.then(
    () => { console.log('this is success callback') }
).catch(
    (err) => { console.log(err) }
)

.catch()的作用是捕獲Promise的錯誤缸逃,與then()的rejected回調(diào)作用幾乎一致。但是由于Promise的拋錯具有冒泡性質(zhì)厂抽,能夠不斷傳遞需频,這樣就能夠在下一個catch()中統(tǒng)一處理這些錯誤。
同時catch()也能夠捕獲then()中拋出的錯誤筷凤,所以建議不要使用then()的rejected回調(diào)昭殉,而是統(tǒng)一使用catch()來處理錯誤。
同樣藐守,catch()中也可以拋出錯誤挪丢,由于拋出的錯誤會在下一個catch中被捕獲處理,因此可以再添加catch()卢厂。

3乾蓬、Promise.all()

var promise = Promise.all( [p1, p2, p3] )
promise.then(
   //  ...
).catch(
   // ...
)

當(dāng)p1、p2慎恒、p3的狀態(tài)都變成resolved時任内,promise才會變成resolved撵渡,并調(diào)用then()的已完成回調(diào),但只要有一個變成rejected狀態(tài)死嗦,promise就會立刻變成rejected狀態(tài)趋距。

4、Promise.race()

var promise = Promise.race( [p1, p2, p3] )
promise.then(
   // ...
).catch(
   // ...
)

race()方法越除,參數(shù)與Promise.all()相同节腐,不同的是,參數(shù)中的p1廊敌、p2铜跑、p3只要有一個改變狀態(tài),promise就會立刻變成相同的狀態(tài)并執(zhí)行對于的回調(diào)骡澈。

5锅纺、Promise.done()
Promise.done()的用法類似.then() ,可以提供resolved和rejected方法肋殴,也可以不提供任何參數(shù)囤锉,它的主要作用是在回調(diào)鏈的尾端捕捉前面沒有被.catch() 捕捉到的錯誤。

6护锤、Promise.finally()
Promise. finally()接受一個方法作為參數(shù)官地,這個方法不管promise最終的狀態(tài)是怎樣,都一定會被執(zhí)行烙懦。

五驱入、缺點

說了這么多Promise的好用之處,那是不是堪稱完美的呢氯析?不是的亏较,下面列舉了它的幾條缺點,也是我們在使用過程中需要注意的地方掩缓。

  • 無法取消Promise雪情,一旦新建它就會立即執(zhí)行,無法中途取消你辣。
  • 如果不設(shè)置回調(diào)函數(shù)巡通,Promise內(nèi)部拋出的錯誤,不會反應(yīng)到外部舍哄。
  • 當(dāng)處于Pending狀態(tài)時宴凉,無法得知目前進(jìn)展到哪一個階段(剛剛開始還是即將完成)。

六蠢熄、演變(async/await)

Promise讓我們告別回調(diào)函數(shù)跪解,寫出更優(yōu)雅的異步代碼;在實踐過程中签孔,卻發(fā)現(xiàn)Promise并不完美叉讥;技術(shù)進(jìn)步是無止境的,這時饥追,便有了async/await图仓。
使用async/await,搭配promise但绕,可以通過編寫形似同步的代碼來處理異步流程救崔,提高代碼的簡潔性和可讀性。

async
使用async function可以定義一個異步函數(shù)捏顺,不管在函數(shù)體內(nèi)return了什么值六孵,async函數(shù)的實際返回值總是一個Promise對象。

await

await操作符用于等待一個Promise對象幅骄,它只能在異步函數(shù)async function內(nèi)部使用劫窒。

需要注意的是,await表達(dá)式會暫停當(dāng)前async function的執(zhí)行拆座,等待Promise處理完成主巍。若Promise正常處理,其處理結(jié)果作為await表達(dá)式的值挪凑,繼續(xù)執(zhí)行async function孕索;若Promise處理異常,await表達(dá)式會把Promise的異常原因拋出。

相信大家都已經(jīng)在項目中使用過async/await語法了躏碳,那么我就不介紹它的用法了搞旭,直接說說需要注意哪些地方。

  • 避免直接將await調(diào)用當(dāng)作變量菇绵,例如:
// 錯誤寫法
initData(await getData());
// 正確寫法
const data = await getData();
initData(data);

// 錯誤寫法
const obj = {
  data: await getData()
}
// 正確寫法
const data = await getData();
const obj = {
  data
}
  • await只能用于async聲明的函數(shù)上下文中肄渗, 例如:
/* 場景:多個專題獲取并處理報告內(nèi)容 */

// 錯誤寫法
async handleInited() {
    this.reportData.map(v => {
        const reportData = await this.handleReport(v.topicId);
        return reportData;
     });
},
// 正確寫法
handleInited() {
    this.reportData.map(async v => { // 從語法上來說,給map的callback加上async才可以運行
        const reportData = await this.handleReport(v.topicId);
        return reportData;
     });
},
  • 注意異步操作的依賴關(guān)系脸甘,避免濫用async/await恳啥,例如:
/* 場景:多個專題獲取并處理報告內(nèi)容 */

// 錯誤寫法
handleInited() {
    this.reportData.map(async v => {
        const reportData = await this.handleReport(v.topicId);
        return reportData;
     });
     this.showLoading = false; // 并不會等待上面代碼返回結(jié)果后再執(zhí)行
},
// 正確寫法
async handleInited() {
    Promise.all(this.reportData.map(v => {
        return this.handleReport(v.topicId);
     })).then(() => {
        this.showLoading = false;
    });
},

從上面的代碼可以看出,我們是想要handleReport函數(shù)并行執(zhí)行丹诀,等到都返回結(jié)果后再將showLoading置為false钝的。如果使用async/await,顯然不能達(dá)到我們想要的铆遭,await只會暫停mapcallback硝桩,因此map完成時,不能保證handleReport也全部完成枚荣,這時使用之前提到的Promise.all()再合適不過了碗脊。

七、總結(jié)

JavaScript的異步編寫方式橄妆,從回調(diào)函數(shù)到Promise再到async/await衙伶,表面上只是寫法的變化祈坠,本質(zhì)上則是語言層的一次次抽象,讓我們可以用更簡單的方式實現(xiàn)同樣的功能矢劲,而不需要去考慮代碼是如何執(zhí)行的赦拘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市芬沉,隨后出現(xiàn)的幾起案子躺同,更是在濱河造成了極大的恐慌,老刑警劉巖丸逸,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹋艺,死亡現(xiàn)場離奇詭異,居然都是意外死亡黄刚,警方通過查閱死者的電腦和手機(jī)捎谨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隘击,“玉大人侍芝,你說我怎么就攤上這事÷裢” “怎么了州叠?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凶赁。 經(jīng)常有香客問我咧栗,道長,這世上最難降的妖魔是什么虱肄? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任致板,我火速辦了婚禮,結(jié)果婚禮上咏窿,老公的妹妹穿的比我還像新娘斟或。我一直安慰自己,他們只是感情好集嵌,可當(dāng)我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布萝挤。 她就那樣靜靜地躺著,像睡著了一般根欧。 火紅的嫁衣襯著肌膚如雪怜珍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天凤粗,我揣著相機(jī)與錄音酥泛,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛柔袁,可吹牛的內(nèi)容都是我干的呆躲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瘦馍,長吁一口氣:“原來是場噩夢啊……” “哼歼秽!你這毒婦竟也來了应役?” 一聲冷哼從身側(cè)響起情组,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎箩祥,沒想到半個月后院崇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡袍祖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年底瓣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕉陋。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡捐凭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凳鬓,到底是詐尸還是另有隱情茁肠,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布缩举,位于F島的核電站垦梆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏仅孩。R本人自食惡果不足惜托猩,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辽慕。 院中可真熱鬧京腥,春花似錦、人聲如沸溅蛉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽温艇。三九已至因悲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勺爱,已是汗流浹背晃琳。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卫旱。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓人灼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親顾翼。 傳聞我的和親對象是個殘疾皇子投放,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,654評論 2 354

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

  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,706評論 1 56
  • Promise對象是一種解決異步問題的方法适贸,還有的解決方案是asyns 和 await (es7) 這么是目前的終...
    站在大神的肩膀上看世界閱讀 1,264評論 0 6
  • 說到異步,怎么說還是得有Promise這玩意 Promise 的含義 Promise 是一個保存著某個未來才會結(jié)束...
    LElysion閱讀 672評論 0 0
  • 目錄:Promise 的含義基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry閱讀 1,494評論 0 8
  • 執(zhí)簡書之手灸芳,盡余生之力,滌塵土之垢拜姿,視人間之欲烙样,思入世之秘,留真情給你蕊肥。你若來過谒获,我變記得。努力更文于活著的每一天壁却!
    綺靈兒閱讀 348評論 6 4