淺析Javascript ES6中的原生Promise
來(lái)源:易賢網(wǎng) 閱讀:1110 次 日期:2016-08-30 14:27:25
溫馨提示:易賢網(wǎng)小編為您整理了“淺析Javascript ES6中的原生Promise”,方便廣大網(wǎng)友查閱!

前言

一個(gè) Promise 對(duì)象可以理解為一次將要執(zhí)行的操作(常常被用于異步操作),使用了 Promise 對(duì)象之后可以用一種鏈?zhǔn)秸{(diào)用的方式來(lái)組織代碼,讓代碼更加直觀。而且由于 Promise.all 這樣的方法存在,可以讓同時(shí)執(zhí)行多個(gè)操作變得簡(jiǎn)單。

Promise的興起,是因?yàn)楫惒椒椒ㄕ{(diào)用中,往往會(huì)出現(xiàn)回調(diào)函數(shù)一環(huán)扣一環(huán)的情況。這種情況導(dǎo)致了回調(diào)金字塔問題的出現(xiàn)。不僅代碼寫起來(lái)費(fèi)勁又不美觀,而且問題復(fù)雜的時(shí)候,閱讀代碼的人也難以理解。 

舉例如下:

db.save(data, function(data){

 // do something...

 db.save(data1, function(data){

 // do something...

 db.save(data2, function(data){

  // do something...

  done(data3); // 返回?cái)?shù)據(jù)

 })

 });

});

假設(shè)有一個(gè)數(shù)據(jù)庫(kù)保存操作,一次請(qǐng)求需要在三個(gè)表中保存三次數(shù)據(jù)。那么我們的代碼就跟上面的代碼相似了。這時(shí)候假設(shè)在第二個(gè)db.save出了問題怎么辦?基于這個(gè)考慮,我們又需要在每一層回調(diào)中使用類似try...catch這樣的邏輯。這個(gè)就是萬(wàn)惡的來(lái)源,也是node剛開始廣為詬病的一點(diǎn)。

另外一個(gè)缺點(diǎn)就是,假設(shè)我們的三次保存之間并沒有前后依賴關(guān)系,我們?nèi)匀恍枰却懊娴暮瘮?shù)執(zhí)行完畢, 才能執(zhí)行下一步,而無(wú)法三個(gè)保存并行,之后返回一個(gè)三個(gè)保存過后需要的結(jié)果。(或者說(shuō)實(shí)現(xiàn)起來(lái)需要技巧)

不幸的是,在我剛開始接觸node的時(shí)候,我寫了大量這樣的hell。

后來(lái)因?yàn)檫€是寫前端代碼多一些,我接觸了ES6,發(fā)現(xiàn)了一個(gè)解決回調(diào)深淵的利器Promise。

其實(shí)早在ES6的Promise之前,Q,when.js,bluebird等等庫(kù)早就根據(jù)Promise標(biāo)準(zhǔn)(參考Promise/A+)造出了自己的promise輪子。 

(看過一篇文章,我覺得很有道理。里面說(shuō),不要擴(kuò)展內(nèi)置的原生對(duì)象。這種做法是不能面向未來(lái)的。所以這里有一個(gè)提示:使用擴(kuò)展原生Promise的庫(kù)時(shí),需要謹(jǐn)慎。)

這里僅討論原生的Promise。

ES6 Promise

Promise對(duì)象狀態(tài)

在詳解Promise之前,先來(lái)點(diǎn)理論:

Promise/A+規(guī)范, 規(guī)定Promise對(duì)象是一個(gè)有限狀態(tài)機(jī)。

它三個(gè)狀態(tài):

1、pending(執(zhí)行中)

2、fulfilled(成功)

3、reject(拒絕)

其中pending為初始狀態(tài),fulfilled和rejected為結(jié)束狀態(tài)(結(jié)束狀態(tài)表示promise的生命周期已結(jié)束)。

狀態(tài)轉(zhuǎn)換關(guān)系為:

pending->fulfilled,pending->rejected。

隨著狀態(tài)的轉(zhuǎn)換將觸發(fā)各種事件(如執(zhí)行成功事件、執(zhí)行失敗事件等)。

Promise形式

Promise的長(zhǎng)相就像這樣子:

var promise = new Promise(function func(resolve, reject){

 // do somthing, maybe async

 if (success){

 return resolve(data);

 } else {

 return reject(data);

 }

});

promise.then(function(data){

 // do something... e.g

 console.log(data);

}, function(err){

 // deal the err.

})

這里的變量promise是Promise這個(gè)對(duì)象的實(shí)例。

promise對(duì)象在創(chuàng)建的時(shí)候會(huì)執(zhí)行func函數(shù)中的邏輯。

邏輯處理完畢并且沒有錯(cuò)誤時(shí),resolve這個(gè)回調(diào)會(huì)將值傳遞到一個(gè)特殊的地方。這個(gè)特殊的地方在哪呢?就是下面代碼中的then,我們使用then中的回調(diào)函數(shù)來(lái)處理resolve后的結(jié)果。比如上面的代碼中,我們將值簡(jiǎn)單的輸出到控制臺(tái)。如果有錯(cuò)誤,則reject到then的第二個(gè)回調(diào)函數(shù)中,對(duì)錯(cuò)誤進(jìn)行處理。

配合上面的有限狀態(tài)機(jī)的理論,我們知道在Promise構(gòu)造函數(shù)中執(zhí)行回調(diào)函數(shù)代碼時(shí),狀態(tài)為pending,resolve之后狀態(tài)為fulfilled,reject之后狀態(tài)為reject

Promise數(shù)據(jù)流動(dòng)

以上是promise的第一次數(shù)據(jù)流動(dòng)情況。

比較funny的是,promise的then方法依然能夠返回一個(gè)Promise對(duì)象,這樣我們就又能用下一個(gè)then來(lái)做一樣的處理。

第一個(gè)then中的兩個(gè)回調(diào)函數(shù)決定第一個(gè)then返回的是一個(gè)什么樣的Promise對(duì)象。

假設(shè)第一個(gè)then的第一個(gè)回調(diào)沒有返回一個(gè)Promise對(duì)象,那么第二個(gè)then的調(diào)用者還是原來(lái)的Promise對(duì)象,只不過其resolve的值變成了第一個(gè)then中第一個(gè)回調(diào)函數(shù)的返回值。

假設(shè)第一個(gè)then的第一個(gè)回調(diào)函數(shù)返回了一個(gè)Promise對(duì)象,那么第二個(gè)then的調(diào)用者變成了這個(gè)新的Promise對(duì)象,第二個(gè)then等待這個(gè)新的Promise對(duì)象resolve或者reject之后執(zhí)行回調(diào)。

話雖然饒了一點(diǎn),但是我自我感覺說(shuō)的還是很清楚的呢。哈哈~

如果任意地方遇到了錯(cuò)誤,則錯(cuò)誤之后交給遇到的第一個(gè)帶第二個(gè)回調(diào)函數(shù)的then的第二個(gè)回調(diào)函數(shù)來(lái)處理。可以理解為錯(cuò)誤一直向后reject, 直到被處理為止。

另外,Promise對(duì)象還有一個(gè)方法catch,這個(gè)方法接受一個(gè)回調(diào)函數(shù)來(lái)處理錯(cuò)誤。

即:

promise.catch(function(err){

 // deal the err.

})

假設(shè)對(duì)錯(cuò)誤的處理是相似的,這個(gè)方法可以對(duì)錯(cuò)誤進(jìn)行集中統(tǒng)一處理。所以其他的then方法就不需要第二個(gè)回調(diào)啦~

控制并發(fā)的Promise

Promise有一個(gè)"靜態(tài)方法"——Promise.all(注意并非是promise.prototype), 這個(gè)方法接受一個(gè)元素是Promise對(duì)象的數(shù)組。

這個(gè)方法也返回一個(gè)Promise對(duì)象,如果數(shù)組中所有的Promise對(duì)象都resolve了,那么這些resolve的值將作為一個(gè)數(shù)組作為Promise.all這個(gè)方法的返回值的(Promise對(duì)象)的resolve值,之后可以被then方法處理。如果數(shù)組中任意的Promise被reject,那么該reject的值就是Promise.all方法的返回值的reject值.

很op的一點(diǎn)是: 

then方法的第一個(gè)回調(diào)函數(shù)接收的resolve值(如上所述,是一個(gè)數(shù)組)的順序和Promise.all中參數(shù)數(shù)組的順序一致,而不是按時(shí)間順序排序。

還有一個(gè)和Promise.all相類似的方法Promise.race,它同樣接收一個(gè)數(shù)組,只不過它只接受第一個(gè)被resolve的值。

將其他對(duì)象變?yōu)镻romise對(duì)象

Promise.resovle方法,可以將不是Promise對(duì)象作為參數(shù),返回一個(gè)Promise對(duì)象。

有兩種情形:

假設(shè)傳入的參數(shù)沒有一個(gè).then方法,那么這個(gè)返回的Promise對(duì)象變成了resolve狀態(tài),其resolve的值就是這個(gè)對(duì)象本身。

假設(shè)傳入的參數(shù)帶有一個(gè)then方法(稱為thenable對(duì)象), 那么將這個(gè)對(duì)象的類型變?yōu)镻romise,其then方法變成Promise.prototype.then方法。

Promise是解決異步的方案嗎?

最后說(shuō)一點(diǎn)很重要的事:Promise的作用是解決回調(diào)金字塔的問題,對(duì)于控制異步流程實(shí)際上沒有起到很大的作用。真正使用Promise對(duì)異步流程進(jìn)行控制,我們還要借助ES6 generator函數(shù)。(例如Tj大神的co庫(kù)的實(shí)現(xiàn))。

然而ES7將有一個(gè)更加牛逼的解決方案:async/await,這個(gè)方案類似于co,但是加了原生支持。拭目以待吧。

總結(jié)

以上就是關(guān)于Javascript ES6中原生Promise的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家學(xué)習(xí)ES6能有所幫助

更多信息請(qǐng)查看網(wǎng)絡(luò)編程
易賢網(wǎng)手機(jī)網(wǎng)站地址:淺析Javascript ES6中的原生Promise
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

2025國(guó)考·省考課程試聽報(bào)名

  • 報(bào)班類型
  • 姓名
  • 手機(jī)號(hào)
  • 驗(yàn)證碼
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢 | 簡(jiǎn)要咨詢須知 | 新媒體/短視頻平臺(tái) | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專用圖標(biāo)
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專用圖標(biāo)