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