摘要:如果有錯誤,則到的第二個回調(diào)函數(shù)中,對錯誤進行處理。假設(shè)第一個的第一個回調(diào)沒有返回一個對象,那么第二個的調(diào)用者還是原來的對象,只不過其的值變成了第一個中第一個回調(diào)函數(shù)的返回值。
ES6標準出爐之前,一個幽靈,回調(diào)的幽靈,游蕩在JavaScript世界。
正所謂:
世界本沒有回調(diào),寫的人多了,也就有了})})})})})。
Promise的興起,是因為異步方法調(diào)用中,往往會出現(xiàn)回調(diào)函數(shù)一環(huán)扣一環(huán)的情況。這種情況導致了回調(diào)金字塔問題的出現(xiàn)。不僅代碼寫起來費勁又不美觀,而且問題復(fù)雜的時候,閱讀代碼的人也難以理解。
舉例如下:
db.save(data, function(data){ // do something... db.save(data1, function(data){ // do something... db.save(data2, function(data){ // do something... done(data3); // 返回數(shù)據(jù) }) }); });
假設(shè)有一個數(shù)據(jù)庫保存操作,一次請求需要在三個表中保存三次數(shù)據(jù)。那么我們的代碼就跟上面的代碼相似了。這時候假設(shè)在第二個db.save出了問題怎么辦?基于這個考慮,我們又需要在每一層回調(diào)中使用類似try...catch這樣的邏輯。這個就是萬惡的來源,也是node剛開始廣為詬病的一點。
另外一個缺點就是,假設(shè)我們的三次保存之間并沒有前后依賴關(guān)系,我們?nèi)匀恍枰却懊娴暮瘮?shù)執(zhí)行完畢, 才能執(zhí)行下一步,而無法三個保存并行,之后返回一個三個保存過后需要的結(jié)果。(或者說實現(xiàn)起來需要技巧)
不幸的是,在我剛開始接觸node的時候,我寫了大量這樣的hell。
作為一個有時還動下腦子的程序員,我嘗試了樸靈大人的eventproxy。后來因為還是寫前端代碼多一些,我接觸了ES6,發(fā)現(xiàn)了一個解決回調(diào)深淵的利器Promise。
其實早在ES6的Promise之前,Q,when.js,bluebird等等庫早就根據(jù)Promise標準(參考Promise/A+)造出了自己的promise輪子。
(看過一篇文章,我覺得很有道理。里面說,不要擴展內(nèi)置的原生對象。這種做法是不能面向未來的。所以這里有一個提示:使用擴展原生Promise的庫時,需要謹慎。)
這里僅討論原生的Promise。
ES6 Promise Promise對象狀態(tài)在詳解Promise之前,先來點理論:
Promise/A+規(guī)范, 規(guī)定Promise對象是一個有限狀態(tài)機。它三個狀態(tài):
pending(執(zhí)行中)
fulfilled(成功)
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的長相就像這樣子:
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這個對象的實例。
promise對象在創(chuàng)建的時候會執(zhí)行func函數(shù)中的邏輯。
邏輯處理完畢并且沒有錯誤時,resolve這個回調(diào)會將值傳遞到一個特殊的地方。這個特殊的地方在哪呢?就是下面代碼中的then,我們使用then中的回調(diào)函數(shù)來處理resolve后的結(jié)果。比如上面的代碼中,我們將值簡單的輸出到控制臺。如果有錯誤,則reject到then的第二個回調(diào)函數(shù)中,對錯誤進行處理。
配合上面的有限狀態(tài)機的理論,我們知道在Promise構(gòu)造函數(shù)中執(zhí)行回調(diào)函數(shù)代碼時,狀態(tài)為pending,resolve之后狀態(tài)為fulfilled,reject之后狀態(tài)為reject
Promise數(shù)據(jù)流動以上是promise的第一次數(shù)據(jù)流動情況。
比較funny的是,promise的then方法依然能夠返回一個Promise對象,這樣我們就又能用下一個then來做一樣的處理。
第一個then中的兩個回調(diào)函數(shù)決定第一個then返回的是一個什么樣的Promise對象。
假設(shè)第一個then的第一個回調(diào)沒有返回一個Promise對象,那么第二個then的調(diào)用者還是原來的Promise對象,只不過其resolve的值變成了第一個then中第一個回調(diào)函數(shù)的返回值。
假設(shè)第一個then的第一個回調(diào)函數(shù)返回了一個Promise對象,那么第二個then的調(diào)用者變成了這個新的Promise對象,第二個then等待這個新的Promise對象resolve或者reject之后執(zhí)行回調(diào)。
話雖然饒了一點,但是我自我感覺說的還是很清楚的呢。哈哈~
如果任意地方遇到了錯誤,則錯誤之后交給遇到的第一個帶第二個回調(diào)函數(shù)的then的第二個回調(diào)函數(shù)來處理??梢岳斫鉃殄e誤一直向后reject, 直到被處理為止。
另外,Promise對象還有一個方法catch,這個方法接受一個回調(diào)函數(shù)來處理錯誤。即:
promise.catch(function(err){ // deal the err. })
假設(shè)對錯誤的處理是相似的,這個方法可以對錯誤進行集中統(tǒng)一處理。所以其他的then方法就不需要第二個回調(diào)啦~
控制并發(fā)的PromisePromise有一個"靜態(tài)方法"——Promise.all(注意并非是promise.prototype), 這個方法接受一個元素是Promise對象的數(shù)組。
這個方法也返回一個Promise對象,如果數(shù)組中所有的Promise對象都resolve了,那么這些resolve的值將作為一個數(shù)組作為Promise.all這個方法的返回值的(Promise對象)的resolve值,之后可以被then方法處理。如果數(shù)組中任意的Promise被reject,那么該reject的值就是Promise.all方法的返回值的reject值.
很op的一點是:
then方法的第一個回調(diào)函數(shù)接收的resolve值(如上所述,是一個數(shù)組)的順序和Promise.all中參數(shù)數(shù)組的順序一致,而不是按時間順序排序。
還有一個和Promise.all相類似的方法Promise.race,它同樣接收一個數(shù)組,只不過它只接受第一個被resolve的值。
將其他對象變?yōu)镻romise對象Promise.resovle方法,可以將不是Promise對象作為參數(shù),返回一個Promise對象。
有兩種情形:
假設(shè)傳入的參數(shù)沒有一個.then方法,那么這個返回的Promise對象變成了resolve狀態(tài),其resolve的值就是這個對象本身。
假設(shè)傳入的參數(shù)帶有一個then方法(稱為thenable對象), 那么將這個對象的類型變?yōu)?b>Promise,其then方法變成Promise.prototype.then方法。
Promise是解決異步的方案嗎?最后說一點很重要的事:Promise的作用是解決回調(diào)金字塔的問題,對于控制異步流程實際上沒有起到很大的作用。真正使用Promise對異步流程進行控制,我們還要借助ES6 generator函數(shù)。(例如Tj大神的co庫的實現(xiàn))。
然而ES7將有一個更加牛逼的解決方案:async/await,這個方案類似于co,但是加了原生支持。拭目以待吧。
文檔mozilla開發(fā)者文檔
以上。一點微小的見解,謝謝大家。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/80182.html
摘要:作為目前最火的模式實現(xiàn)之一,它有很多的點值得研究。這個函數(shù)既然要用于,也就是說它接收一個形式為的函數(shù),對其一層層嵌套形式為。這個會在開始時發(fā)起一個,并在這個時發(fā)起另一個成功或失敗的。為了方便起見,會返回這個讓調(diào)用者可以等待。 Redux作為目前最火的Flux模式實現(xiàn)之一,它有很多的點值得研究。今天我們首先來看看它的Middleware。 熟悉Express或者koa的朋友對Middle...
摘要:我們可以進行適當?shù)母倪M,把回調(diào)函數(shù)寫到外面即使是改寫成這樣,代碼還是不夠直觀,但是如果有了對象,代碼就可以寫得非常清晰,一目了然,請看這樣函數(shù)就不用寫在的回調(diào)中了目前的標準中還未支持對象,那么我們就自己動手,豐衣足食吧。 本文同步自我得博客:http://www.joeray61.com 很多做前端的朋友應(yīng)該都聽說過Promise(或者Deferred)對象,今天我就講一下我對Prom...
摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒有換工作的打算 但為了跟上時代的腳步 還是忍不住整理了一份最新前端知識點 知識點匯總 1.HTML HTML5新特性,語義化瀏覽器的標準模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標簽canvasHTML廢棄的標簽IE6 bug,和一些定位寫法css js放置位置和原因...
摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒有換工作的打算 但為了跟上時代的腳步 還是忍不住整理了一份最新前端知識點 知識點匯總 1.HTML HTML5新特性,語義化瀏覽器的標準模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標簽canvasHTML廢棄的標簽IE6 bug,和一些定位寫法css js放置位置和原因...
摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒有換工作的打算 但為了跟上時代的腳步 還是忍不住整理了一份最新前端知識點 知識點匯總 1.HTML HTML5新特性,語義化瀏覽器的標準模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標簽canvasHTML廢棄的標簽IE6 bug,和一些定位寫法css js放置位置和原因...
閱讀 1724·2023-04-25 14:12
閱讀 1207·2021-08-27 16:24
閱讀 2623·2019-08-30 15:44
閱讀 2976·2019-08-30 13:16
閱讀 1760·2019-08-29 14:10
閱讀 1040·2019-08-29 13:54
閱讀 1378·2019-08-29 13:09
閱讀 1916·2019-08-26 18:37