摘要:直到最近,我們仍然在用簡單的回調函數來處理異步的問題。當我們只有一個異步任務的時候使用回調函數看起來還不會有什么問題。
原文地址:http://blog.getify.com/promis...
廈門旅行歸來,繼續理解Promise
在上一篇深入理解Promise五部曲:1.異步問題中,我們揭示了JS的異步事件輪詢并發模型并且解釋了多任務是如何相互穿插使得它們看起來像是同時運行的。然后我們討論了為什么我們努力地在我們的代碼里表達這些東西以及為什么我們的大腦不善于理解它們。
我們現在要找出一個更好的方式來表達異步流程,然后看看Promises是怎么解決這個問題的。
回調嵌套JS從一開始就使用事件輪詢的并發模型。我們一直以來都在寫異步的程序。直到最近,我們仍然在用簡單的回調函數來處理異步的問題。
makeAjaxRequest(url,function(respnose){ alert("Response:" + response) ; }) ;
當我們只有一個異步任務的時候使用回調函數看起來還不會有什么問題。但是,實際是我們完成一個任務通常需要多個異步操作。例如:
btn.addEventListener("click",function(evt){ makeAjaxRequest(url,function(response){ makeAjaxRequest(anotherURL + "?resp=" + response,function(response2){ alert("Response2:" + response) ; }) }) ; },false) ;
把一系列異步操作鏈接在一起最自然的方式就是使用回調嵌套,步驟2嵌套在步驟1中然后步驟3嵌套在步驟2中,等等。
回調地獄你使用越多的回調,就會有越多的嵌套,不斷縮進意大利面條似的代碼。很顯然,這種代碼難以編寫,難以理解而且難以維護。如果我們花點時間來理清這些代碼往往會讓我們事半功倍。這類嵌套/縮進經常被叫做"回調地獄"。有時也被叫做"回調金字塔",專指由于代碼不斷縮進所形成的金字塔形狀,縮進越多金字塔形狀越明顯。
但是我還是覺得"回調地獄"真的跟嵌套和縮進扯不上太大的關系。如果之前有人跟你說回調地獄就是指嵌套和縮進的話,不要相信他,因為他們并不理解回調真正的問題在哪兒。
可靠性缺失回調(無論是否有嵌套)的真正問題是遠比編輯器中的空白符嚴重。讓我們來分析下下面這個簡單的回調發生了什么
//1.everything in my program before now someAsyncThing(function(){ //2.everything in my program for later }) ;
你看清這段代碼說了什么嗎?你從根本上把你的程序分成了兩個部分:
直到現在為止發生的事情
以后會發生的事情
換句話說,你把第二部分代碼包裝在一個回調函數中然后延遲到后面執行。
但是這并不是問題,真正問題是在1和2之間發生了什么。請問在這段時間內是誰在控制這些。
someAsyncThing(..)控制著這些。是你自己擁有并管理someAsyncThing()嗎?許多時候不是。更重要的是,你有多信任someAsyncThing(..)?你會問,信任什么?不管你意識到沒有,你潛在的相信someAsyncThing(..)會做到下面這些:
不會太早調用我的回調函數
不會太遲調用我的回調函數(1,2就是說會在適當的時候調用回調函數)
不會調用我的回調太少次(不會少于實際應該調用的次數,比如不會漏掉函數調用)
不會調用我的回調太多次(不會多于實際應該調用的次數,比如重復調用)
會給我的回調提供必要的參數
在我的回調失敗的時候會提醒我
咳!你也太信任它了!
實際上,這里真正的問題是由于回調引起的控制轉移。在你的程序的前半部分,你控制著程序的進程?,F在你轉移了控制權,someAsyncThing(..)控制了你剩余程序什么時候返回以及是否返回。控制轉移表明了你的代碼和其他人的代碼之間的過度信任關系。
恐嚇戰術當someAsyncThing(..)是第三方庫的一個方法并且你無法控制不能檢查的時候會發生什么?只能祝你好運了!
比如你有一個電子商務網站,用戶就要完成付款的步驟了,但是在扣費之前有最后一個步驟,它需要通知一個第三方跟蹤庫。你調用他們API,并且提供一個回調函數。大部分情況下,這不會有什么問題。但是,在這次業務中,有一些你和他們都沒有意識到的奇怪的Bug,結果就是第三方庫在超時之前五秒的時間內每隔一秒就會調用一次回調函數。猜猜發生了什么?在這個回調里調用了chargeTheCreditCard()。
Oops,消費者被扣了五次錢。為什么?因為你相信第三方庫只會調用你的回調一次。
所以你不得不被丟雞蛋并且給消費者道歉歸還多扣的四次錢。然后你立刻采取措施確保這種情況不會再發生。你會怎么做呢?
你可能會創建一些狀態值來跟蹤你的回調,當它被調用一次之后會被標記,然后就可以忽略任何意外的重復調用。無論第三方如何道歉并且承諾他們的bug已經修復了,你再也不會相信他們了,不是嗎?
這看起來像一個愚蠢的場景,但是這可能比你想得還普遍。我們的程序變得越復雜,我們就會集成越多的第三方/外部代碼,這種愚蠢的場景就越容易發生。
布基膠帶你給你的回調加入了狀態跟蹤機制,然后睡了一個好覺。但是實際上你只是處理了信任列表許多項目中的一項。當另一個bug造成另一個可靠性丟失的情況時會發生什么?更多的改造,更多丑陋的代碼。
更多布基膠帶。你必須不斷修復回調中的漏洞。無論你是多優秀的開發者,無論你的布基膠帶多漂亮,事實就是:在你信任墻上的回調充滿了漏洞。
Promise解決方案一些人喜歡使用布基繃帶并且給信任墻上的洞打補丁。但是在某些時候,你也許會問自己,是否有其他模式來表達異步流程控制,不需要忍受所有這些可靠性丟失?
是的!Promises就是一個方法。
在我解釋它們是怎么工作之前,讓我來解釋一些它們背后的概念問題。
快餐業務你走進你最喜愛的快餐店,走到前臺要了一些美味的食物。收銀員告訴你一共7.53美元然后你把錢給她。她會給回你什么東西呢?
如果你足夠幸運,你要的食物已經準備好了。但是大多數情況下,你會拿到一個寫著序列號的小票,是吧?所以你站到一邊等待你的食物。
很快,你聽到廣播響起:“請317號取餐”。正好是你的號碼。你走到前臺用小票換來你的食物!謝天謝地,你不用忍受太長的等待。
剛才發生的是一個對于Promises很好的比喻。你走到前臺開始一個業務,但是這個業務不能馬上完成。所以,你得到一個在遲些時候完成業務(你的食物)的promise(小票)。一旦你的食物準備就緒,你會得到通知然后你第一時間用你的promise(小票)換來了你想要的東西:食物。
換句話說,帶有序列號的小票就是對于一個未來結果的承諾。
完成事件想想上面調用someAsyncThing(..)的例子。如果你可以調用它然后訂閱一個事件,當這個調用完成的時候你會得到通知而不是傳遞一個回調給它,這樣難道不會更好嗎?
例如,想象這樣的代碼:
var listener = someAsyncThing(..) ; listener.on("completion",function(data){ //keep going now ! }) ;
實際上,如果我們還可以監聽調用失敗的事件那就更好了。
listener.on("failure",function(){ //Oops,What"s plan B? }) ;
現在,對于我們調用的每個函數,我們能夠在函數成功執行或者失敗的時候得到通知。換句話說,每個函數調用會是流程控制圖上的決策點。
Promise"事件"Promises就像是一個函數在說“我這有一個事件監聽器,當我完成或者失敗的時候會被通知到?!蔽覀兛纯此窃趺垂ぷ鞯模?/p>
function someAsyncThing(){ var p = new Promise(function(resolve,reject){ //at some later time,call "resolve()" or "reject()" }) ; return p ; } var p = someAsyncThing() ; p.then( function(){ //success happened }, function(){ //failure happened } ) ;
你只需要監聽then事件,然后通過知道哪個回調函數被調用就可以知道是成功還是失敗。
逆轉通過promises,我們重新獲得了程序的控制權而不是通過給第三方庫傳遞回調來轉移控制權。這是javascript中異步控制流程表達上一個很大的進步。
“等等”,你說。“我仍然要傳遞回調啊。有什么不一樣?!”嗯。。。好眼力!
有些人聲稱Promises通過移除回調來解決“回調地獄”的問題。并不是這樣!在一些情況下,你甚至需要比以前更多的回調。同時,根據你如何編寫你的代碼,你可能仍然需要把promises嵌套在別的promises中!
批判性地看,promises所做的只是改變了你傳遞回調的地方。
本質上,如果你把你的回調傳遞給擁有良好保證和可預測性的中立Promises機制,你實質上重新獲得了對于后續程序能很穩定并且運行良好的可靠性。標準的promises機制有以下這些保證:
如果promise被resolve,它要不是success就是failure,不可能同時存在。
一旦promise被resolve,它就再也不會被resolve(不會出現重復調用)。
如果promise返回了成功的信息,那么你綁定在成功事件上的回調會得到這個消息。
如果發生了錯誤,promise會收到一個帶有錯誤信息的錯誤通知。
無論promise最后的結果是什么(success或者failure),他就不會改變了,你總是可以獲得這個消息只要你不銷毀promise。
如果我們從someAsyncThing(..)得到的promise不是可用的標準的promise會發生什么?如果我們無法判斷我們是否可相信它是真的promise會怎么樣?
簡單!只要你得到的是“類promise”的,也就是擁有then(..)方法可以注冊success和failure事件,那么你就可用使用這個“類promise”然后把它包裝在一個你信任的promise中。
var notSureWhatItIs = someAsyncThing(); var p = Promise.resolve( notSureWhatItIs ); // now we can trust `p`!! p.then( function(){ // success happened }, function(){ // failure happened } );
promises的最重要的特點就是它把我們處理任何函數調用的成功或者失敗的方式規范成了可預測的形式,特別是如果這個調用實際上的異步的。
在這個規范過程中,它使我們的程序在可控制的位置而不是把控制權交給一個不可相信的第三方。
總結不要管你所聽到的,“回調地獄”不是真的關于函數嵌套和它們在代碼編輯器中產生的縮進。它是關于控制轉移的,是指我們由于把控制權交給一個我們不能信任的第三方而產生的對我們的程序失去控制的現象。
Promises逆轉了這個情況,它使得我們重新獲得控制權。相比傳遞回調給第三方函數,函數返回一個promise對象,我們可以使用它來監聽函數的成功或失敗。在promise我們仍然使用回調,但是重要的是標準的promise機制使我們可以信任它們行為的正確性。我們不需要想辦法來處理這些可靠性問題。
在第三部分:可靠性問題中,我會說道一個promises可靠性機制中很特別的部分:一個promise的狀態必須是可靠并且不可變的。
深入理解Promise五部曲--1.異步問題
深入理解Promise五部曲--2.轉換問題
深入理解Promise五部曲--3.可靠性問題
深入理解Promise五部曲--4.擴展性問題
深入理解Promise五部曲--5.樂高問題
最后,安利下我的個人博客,歡迎訪問:http://bin-playground.top
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87569.html
摘要:當引擎開始執行一個函數比如回調函數時,它就會把這個函數執行完,也就是說只有執行完這段代碼才會繼續執行后面的代碼。當條件允許時,回調函數就會被運行?,F在,返回去執行注冊的那個回調函數。 原文地址:http://blog.getify.com/promis... 在微博上看到有人分享LabJS作者寫的關于Promise的博客,看了下覺得寫得很好,分五個部分講解了Promise的來龍去脈。從...
摘要:有一個和相關的更大的問題。最后,請負有責任感并且使用安全的擴展。深入理解五部曲異步問題深入理解五部曲轉換問題深入理解五部曲可靠性問題深入理解五部曲擴展性問題深入理解五部曲樂高問題最后,安利下我的個人博客,歡迎訪問 原文地址:http://blog.getify.com/promis... 現在,我希望你已經看過深入理解Promise的前三篇文章了。并且假設你已經完全理解Promises...
摘要:簡單的說,即將到來的標準指出是一個,所以作為一個,必須可以被子類化。保護還是子類化這是個問題我真的希望我能創建一個忠實的給及以下。 原文地址:http://blog.getify.com/promis... 如果你需要趕上我們關于Promise的進度,可以看看這個系列前兩篇文章深入理解Promise五部曲--1.異步問題和深入理解Promise五部曲--2.控制權轉移問題。 Promi...
摘要:一個就像一個樂高玩具。問題是不是你小時候玩兒的那個有趣,它們不是充滿想象力的打氣筒,也不是一種樂高玩具。這是對的并不是給開發者使用的,它們是給庫作者使用的。不會超過這兩種情況。第二個是根據第一個處理函數如何運行來自動變成狀態成功或者失敗。 原文地址:http://blog.getify.com/promis... 在 Part4:擴展問題 中,我討論了如何擴展和抽象Promise是多么...
摘要:只要在調用異步函數時設置一個或多個回調函數,函數就會在完成時自動調用回調函數。要解決的問題是,如何將回調方法的參數從回調方法中傳遞出來,讓它可以像同步函數的返回結果一樣,在回調函數以外的控制范圍內,可以傳遞和復用。 摘要: 我們知道 JavaScript 自從有了 Generator 之后,就有了各種基于 Generator 封裝的協程。其中 hprose 中封裝的 Promise 和...
閱讀 1764·2021-10-11 10:59
閱讀 2402·2021-09-30 09:53
閱讀 1765·2021-09-22 15:28
閱讀 2795·2019-08-29 15:29
閱讀 1558·2019-08-29 13:53
閱讀 3207·2019-08-29 12:34
閱讀 2849·2019-08-26 10:16
閱讀 2661·2019-08-23 15:16