摘要:我的博客大家都知道解決了回調地獄的問題。這就是異步的嵌套帶來的可讀性的問題,它是由異步的運行機制引起的。在與第三方團隊溝通之后問題得到了解決。這不但使代碼變得臃腫不堪,還進一步加劇了可讀性的問題。的特征保證了可以解決信任問題。
我的github博客 https://github.com/zhuanyongxigua/blog
大家都知道Promise解決了回調地獄的問題。說到回調地獄,很容易想到下面這個容易讓人產生誤解的圖片:
可回調地獄到底是什么?它到底哪里有問題?是因為嵌套不好看還是讀起來不方便?
首先我們要想想,嵌套到底哪里有問題?
舉個例子:
function a() { function b() { function c() { function d() {} d(); } c(); } b(); } a();
這也是嵌套,雖然好像不是特別美觀,可我們并不會覺得這有什么問題吧?因為我們經常會寫出類似的代碼。
在這個例子中的嵌套的問題僅僅是縮進的問題,而縮進除了會讓代碼變寬可能會造成讀代碼的一點不方便之外,并沒有什么其他的問題。如果僅僅是這樣,為什么不叫“縮進地獄”或“嵌套地獄”?
把回調地獄完全理解成縮進的問題是常見的對回調地獄的誤解。要回到“回調地獄”這個詞語上面來,它的重點就在于“回調”,而“回調”在JS中應用最多的場景當然就是異步編程了。
所以,“回調地獄”所說的嵌套其實是指異步的嵌套。它帶來了兩個問題:可讀性的問題和信任問題。
可讀性的問題這是一個在網上隨便搜索的關于執行順序的面試題:
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(new Date, i); }, 1000); } console.log(new Date, i);
答案是什么大家自己想吧,這不是重點。重點是,你要想一會兒吧?
一個整潔的回調:
listen( "click", function handler( evt){ setTimeout( function request(){ ajax( "http:// some. url. 1", function response( text){ if (text == "hello") { handler(); } else if (text == "world") { request(); } }); }, 500); });
如果異步的嵌套都是這樣干凈整潔,那“回調地獄”給程序猿帶來的傷害馬上就會減少很多。
可我們實際在寫業務邏輯的時候,真實的情況應該是這樣的:
listen( "click", function handler(evt){ doSomething1(); doSomething2(); doSomething3(); doSomething4(); setTimeout( function request(){ doSomething8(); doSomething9(); doSomething10(); ajax( "http:// some. url. 1", function response( text){ if (text == "hello") { handler(); } else if (text == "world") { request(); } }); doSomething11(); doSomething12(); doSomething13(); }, 500); doSomething5(); doSomething6(); doSomething7(); });
這些“doSomething”有些是異步的,有些是同步。這樣的代碼讀起來會非常的吃力,因為你要不停的思考他們的執行順序,并且還要記在腦袋里面。這就是異步的嵌套帶來的可讀性的問題,它是由異步的運行機制引起的。
信任問題這里主要用異步請求討論。我們在做AJAX請求的時候,一般都會使用一些第三方的工具庫(即便是自己封裝的,也可以在一定程度上理解成第三方的),這就會帶來一個問題:這些工具庫是否百分百的可靠?
一個來自《YDKJS》的例子:一個程序員開發了一個付款的系統,它良好的運行了很長時間。突然有一天,一個客戶在付款的時候信用卡被連續刷了五次。這名程序員在調查了以后發現,一個第三方的工具庫因為某些原因把付款回調執行了五次。在與第三方團隊溝通之后問題得到了解決。
故事講完了,可問題真的解決了嗎?是否還能夠充分的信任這個工具庫?信任依然要有,可完善必要的檢查和錯誤處理勢在必行。當我們解決了這個問題,由于它的啟發,我們還會聯想到其他的問題,比如沒有調用回調。
再繼續想,你會發現,這樣的問題還要好多好多。總結一下可能會出現的問題:
回調過早(一般是異步被同步調用);
回調過晚或沒有回調;
回調次數過多;
等等
加上了這些檢查,強壯之后的代碼可能是這樣的:
listen( "click", function handler( evt){ check1(); doSomething1(); setTimeout( function request(){ check2(); doSomething3(); ajax( "http:// some. url. 1", function response( text){ if (text == "hello") { handler(); } else if (text == "world") { request(); } }); doSomething4(); }, 500); doSomething2(); });
我們都清楚的知道,實際的check要比這里看起來的復雜的多,而且很多很難復用。這不但使代碼變得臃腫不堪,還進一步加劇了可讀性的問題。
雖然這些錯誤出現的概率不大,但我們依然必須要處理。
這就是異步嵌套帶來的信任問題,它的問題的根源在于控制反轉。控制反轉在面向對象中的應用是依賴注入,實現了模塊間的解耦。而在回調中,它就顯得沒有那么善良了,控制權被交給了第三方,由第三方決定什么時候調用回調以及如何調用回調。
一些解決信任問題的嘗試加一個處理錯誤的回調
function success(data) { console. log(data); } function failure(err) { console. error( err ); } ajax( "http:// some. url. 1", success, failure );
nodejs的error-first
function response(err, data) { if (err) { console. error( err ); } else { console. log( data ); } } ajax( "http:// some. url. 1", response );
這兩種方式解決了一些問題,減少了一些工作量, 但是依然沒有徹底解決問題。首先它們的可復用性依然不強,其次,如回調被多次調用的問題依然無法解決。
Promise如何解決這兩個問題Promise已經是原生支持的API了,它已經被加到了JS的規范里面,在各大瀏覽器中的運行機制是相同的。這樣就保證了它的可靠。
如何解決可讀性的問題這一點不用多說,用過Promise的人很容易明白。Promise的應用相當于給了你一張可以把解題思路清晰記錄下來的草稿紙,你不在需要用腦子去記憶執行順序。
如何解決信任問題Promise并沒有取消控制反轉,而是把反轉出去的控制再反轉一次,也就是反轉了控制反轉。
這種機制有點像事件的觸發。它與普通的回調的方式的區別在于,普通的方式,回調成功之后的操作直接寫在了回調函數里面,而這些操作的調用由第三方控制。在Promise的方式中,回調只負責成功之后的通知,而回調成功之后的操作放在了then的回調里面,由Promise精確控制。
Promise有這些特征:只能決議一次,決議值只能有一個,決議之后無法改變。任何then中的回調也只會被調用一次。Promise的特征保證了Promise可以解決信任問題。
對于回調過早的問題,由于Promise只能是異步的,所以不會出現異步的同步調用。即便是在決議之前的錯誤,也是異步的,并不是會產生同步(調用過早)的困擾。
var a = new Promise((resolve, reject) => { var b = 1 + c; // ReferenceError: c is not defined,錯誤會在下面的a打印出來之后報出。 resolve(true); }) console.log(1, a); a.then(res => { console.log(2, res); }) .catch(err => { console.log(err); })
對于回調過晚或沒有調用的問題,Promise本身不會回調過晚,只要決議了,它就會按照規定運行。至于服務器或者網絡的問題,并不是Promise能解決的,一般這種情況會使用Promise的競態APIPromise.race加一個超時的時間:
function timeoutPromise(delay) { return new Promise(function(resolve, reject) { setTimeout(function() { reject("Timeout!"); }, delay); }); } Promise.race([doSomething(), timeoutPromise(3000)]) .then(...) .catch(...);
對于回調次數太少或太多的問題,由于Promise只能被決議一次,且決議之后無法改變,所以,即便是多次回調,也不會影響結果,決議之后的調用都會被忽略。
參考資料:
You Don"t Know JS: Async & Performance
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97402.html
摘要:我們先介紹一下中的的一些調用再結合的應用逐步深入。這就是一些簡單的的調用看起來不多,但是靠這個真得解決了許多必須同步并行的環境本身是一個對象在開始支持。存在兩個回調函數根據個人的需求進行處理。 什么是promise?為什么要在nodejs中使用promise?使用promise到底有什么好處呢?實在太多了,一一說來不如直接上實戰。我們先介紹一下nodejs中的promise的一些調用....
摘要:因為函數返回一個對象,所以可以用于等待一個函數的返回值這也可以說是在等函數,但要清楚,它等的實際是一個返回值。幫我們干了啥作個簡單的比較上面已經說明了會將其后的函數函數表達式或的返回值封裝成一個對象,而會等待這個完成,并將其的結果返回出來。 隨著 Node 7 的發布,越來越多的人開始研究據說是異步編程終級解決方案的 async/await。我第一次看到這組關鍵字并不是在 JavaSc...
摘要:瀏覽器是多進程的,而瀏覽器的內核渲染進程是多線程的。如果已經將回調函數放進任務隊列,但是主線程正在執行一個非常耗時的任務,當這個任務執行完畢后,主線程去任務隊列中取任務,這個時候,就會出現連續執行的情況,也就是說相當于失效了。 前言 ??在刷筆試題的時候,經常會碰到setTimeout的問題,只知道這個是設置定時器;但是考察的重點一般是在一個方法中包含了定時器,定時器中的打印和方法中打...
摘要:當引擎開始執行一個函數比如回調函數時,它就會把這個函數執行完,也就是說只有執行完這段代碼才會繼續執行后面的代碼。當條件允許時,回調函數就會被運行。現在,返回去執行注冊的那個回調函數。 原文地址:http://blog.getify.com/promis... 在微博上看到有人分享LabJS作者寫的關于Promise的博客,看了下覺得寫得很好,分五個部分講解了Promise的來龍去脈。從...
摘要:回調函數少了還好,一旦多了起來而且必須講究執行順序的話,回調函數開始嵌套,那代碼的惡心程度是相當不符合常人的線性思維的。 什么是Promise? 在說Promise之前, 不得不說一下,JavaScript的嵌套的回調函數 在JavaScript語言中, 無論是寫瀏覽器端的各種事件回調、ajax回調,還是寫Node.js上的業務邏輯,不得不面對的問題就是各種回調函數。回調函數少了還好,...
閱讀 2830·2021-11-24 09:39
閱讀 4082·2021-10-27 14:19
閱讀 2043·2021-08-12 13:25
閱讀 2334·2019-08-29 17:07
閱讀 1112·2019-08-29 13:44
閱讀 1066·2019-08-26 12:17
閱讀 462·2019-08-23 17:16
閱讀 2048·2019-08-23 16:46