摘要:當(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的來龍去脈。從這篇文章開始,我會(huì)陸續(xù)把五篇博客翻譯出來跟大家分享,在大牛的帶領(lǐng)下真正理解Promise。賣個(gè)關(guān)子,作者看待Promise的角度跟我一直以來看到的講解Promise的角度完全不一樣,不只是定留在解決回調(diào)金字塔上,至少我沒想到Promise竟然有這么重要的意義。先上第一篇。
在這篇文章中,我會(huì)解釋我們?yōu)槭裁葱枰褂靡粋€(gè)更好的方式(比如Promise)來進(jìn)行異步流程的編寫。
異步你肯定聽說過Javascript中的異步編程,但是它到底是什么呢?
比如當(dāng)你發(fā)生一個(gè)Ajax請(qǐng)求,你通常會(huì)提供一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)會(huì)在請(qǐng)求返回的時(shí)候被調(diào)用。但是你是否思考過你的回調(diào)函數(shù)在其他代碼也需要運(yùn)行的時(shí)候是如何被調(diào)用的呢?如果兩個(gè)回調(diào)函數(shù)同時(shí)都要運(yùn)行會(huì)怎樣呢?JS引擎會(huì)如何處理這個(gè)問題呢?
為了理解異步到底是什么,你首先需要理解一個(gè)問題:JS引擎是單線程的。這意味著在任何環(huán)境中,只有一段JS代碼會(huì)被執(zhí)行。但是什么叫一段JS代碼呢?總的來說,每個(gè)函數(shù)是一個(gè)不可分割的片段或者代碼塊。當(dāng)JS引擎開始執(zhí)行一個(gè)函數(shù)(比如回調(diào)函數(shù))時(shí),它就會(huì)把這個(gè)函數(shù)執(zhí)行完,也就是說只有執(zhí)行完這段代碼才會(huì)繼續(xù)執(zhí)行后面的代碼。
換句話說,JS引擎就像一個(gè)主題公園中的游樂項(xiàng)目,這個(gè)項(xiàng)目每次只能一個(gè)人玩兒,人們會(huì)排成一個(gè)長(zhǎng)長(zhǎng)的隊(duì)。大家一個(gè)個(gè)上去玩兒,下來一個(gè)然后再上去一個(gè)。如果你要玩兒這個(gè)項(xiàng)目你只能在隊(duì)尾排隊(duì)等待。幸運(yùn)的是,每個(gè)人都很快就下來了,所以這個(gè)隊(duì)伍移動(dòng)得很快。
上面說的隊(duì)伍在技術(shù)上被叫做事件輪詢。它盡可能快的進(jìn)行輪詢,如果事件隊(duì)列中有代碼需要執(zhí)行,它會(huì)讓JS引擎執(zhí)行這段代碼,然后移到下一個(gè)需要執(zhí)行的代碼,或者等待新的代碼進(jìn)來。
并發(fā)如果程序在一個(gè)時(shí)間只有一個(gè)任務(wù)在執(zhí)行,這樣明顯是低效而且有限制性的。如果你點(diǎn)擊一個(gè)按鈕提交一個(gè)表單,然后你的鼠標(biāo)就會(huì)被凍結(jié)并且你不能滾動(dòng)頁(yè)面,這個(gè)情況會(huì)持續(xù)幾秒直到請(qǐng)求返回,這樣肯定會(huì)帶來很差的用戶體驗(yàn)。
這就是為什么真實(shí)的程序會(huì)有很多任務(wù)在運(yùn)行而不是就只有一個(gè)任務(wù),但是JS引擎是怎么在單線程的環(huán)境下實(shí)現(xiàn)的呢?
你應(yīng)該想到每個(gè)代碼塊運(yùn)行只要很短的時(shí)間,通常不到1毫秒。你一眨眼的時(shí)間,JS引擎會(huì)執(zhí)行上千百個(gè)這樣的代碼塊。但是并不是所有的代碼塊都是為了執(zhí)行同一個(gè)任務(wù)。比如,當(dāng)你點(diǎn)擊提交按鈕之后,你也可以點(diǎn)擊導(dǎo)航或者滾動(dòng)頁(yè)面等等。每個(gè)任務(wù)都會(huì)被分為很多個(gè)原子操作,執(zhí)行這些原子操作會(huì)非??臁?/p>
比如:
Task A
step1
step2
step3
step4
Task B
step1
step2
JS引擎肯定不能在執(zhí)行A:1步驟的同時(shí)執(zhí)行B:1。但是Task B不需要等到Task A執(zhí)行完后再執(zhí)行,因?yàn)橐婵梢栽诿總€(gè)獨(dú)立的原子操作之間快速的切換,可能是按下面的順序執(zhí)行的:
A:1
B:1
A:2
B:2(Task B完成)
A:3
A:4(Task A完成)
所以,事實(shí)上Task A和Task B是可以"同時(shí)"運(yùn)行的,通過穿插地執(zhí)行它們的每個(gè)原子操作,這叫做并發(fā),換句話說,Task A和Task B是并發(fā)的。
我們很容易就會(huì)把并發(fā)和并行弄混。在真正并行的系統(tǒng)中,你會(huì)有多個(gè)線程,可能一個(gè)線程執(zhí)行Task A同時(shí)另一個(gè)線程執(zhí)行Task B。這也意味著,A:1的運(yùn)行不會(huì)阻塞B:1的運(yùn)行。這就好像有主題公園中有兩個(gè)分開的游樂項(xiàng)目,會(huì)有兩隊(duì)人在排隊(duì),它們互相不影響。
JS事件輪詢是一個(gè)簡(jiǎn)單的并發(fā)模型。它只允許把每個(gè)事件添加到事件隊(duì)列的隊(duì)尾,而這個(gè)隊(duì)列是先進(jìn)先出的。當(dāng)條件允許時(shí),回調(diào)函數(shù)就會(huì)被運(yùn)行。
同步情況下的異步在JS中編寫異步代碼一個(gè)巧妙但是煩惱的問題是JS引擎實(shí)際執(zhí)行代碼的方式跟我們看上去不大一樣。例如:
makeAjaxRequest(url,function(response){ alert("Response:" + response) ; }) ;
你會(huì)怎么描述這段代碼的流程呢?大多數(shù)開發(fā)者大概會(huì)這么說:
發(fā)送Ajax請(qǐng)求
等到請(qǐng)求完成的時(shí)候,彈出提示框
但是這跟JS引擎實(shí)際的執(zhí)行情況相比還不夠準(zhǔn)確。這個(gè)問題主要是因?yàn)槲覀兇竽X習(xí)慣同步的方式。在上面這個(gè)描述中,我們使用“等到。。。的時(shí)候”來解釋,這就也是說我們會(huì)阻塞等待Ajax請(qǐng)求,然后繼續(xù)執(zhí)行后面的程序。
JS在步驟1和步驟2之間不會(huì)阻塞。一個(gè)更準(zhǔn)確的描述上面這段代碼的方式是:
發(fā)送Ajax請(qǐng)求
注冊(cè)回調(diào)函數(shù)
繼續(xù)向下執(zhí)行
在未來某個(gè)時(shí)間點(diǎn),驚呼“Oh,我剛才得到一個(gè)返回!”。現(xiàn)在,返回去執(zhí)行注冊(cè)的那個(gè)回調(diào)函數(shù)。
這兩個(gè)解釋的區(qū)別似乎沒什么大不了的,但是我們跳過第三步的思考方式是一個(gè)大問題。
源代碼是給開發(fā)者的而不是計(jì)算機(jī)的。計(jì)算機(jī)只關(guān)心1和0.有無(wú)限種程序能產(chǎn)生一樣的1和0序列。我們編寫源代碼為了使得我們能夠以一種有含義并且準(zhǔn)確的方式理解代碼是干嘛的。由于我們的大腦很難處理異步,所以我們需要找出一種更加同步的方式來編寫異步代碼,隱藏具體的異步實(shí)現(xiàn)。
例如,如果下面這段代碼能像我們需要的那樣運(yùn)行并且不會(huì)阻塞,那么它是不是更好理解了呢?
response = makeAjaxRequest(url) ; alert("Response:" + response) ;
如果我們可以像這樣編碼,那么我們就可以隱藏或者抽象makeAjaxRequest()的異步本質(zhì),不需要擔(dān)心具體細(xì)節(jié)。
換句話說,我們能使得異步代碼只出現(xiàn)在具體的實(shí)現(xiàn)上,把這些煩人的東西埋在屬于它的地方。
我們還沒有解決問題。但是至少我們知道了問題是什么:用異步的方式來表達(dá)異步的代碼是艱難的,甚至很難用我們的大腦來理解。
我們需要的只是一種以同步的代碼來盡可能隱藏具體的異步實(shí)現(xiàn)的方式,這樣我們的大腦更好理解。我們的目標(biāo)是以同步的方式來編碼而不需要關(guān)系它的實(shí)現(xiàn)的同步還是異步。
在第二部分:轉(zhuǎn)換的問題中,我會(huì)著手處理“回調(diào)地獄”來解釋這些問題,我們也將看到Promises是如何搞定它的。
深入理解Promise五部曲--1.異步問題
深入理解Promise五部曲--2.轉(zhuǎn)換問題
深入理解Promise五部曲--3.可靠性問題
深入理解Promise五部曲--4.擴(kuò)展性問題
深入理解Promise五部曲--5.樂高問題
最后,安利下我的個(gè)人博客,歡迎訪問:http://bin-playground.top
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78171.html
摘要:直到最近,我們?nèi)匀辉谟煤?jiǎn)單的回調(diào)函數(shù)來處理異步的問題。當(dāng)我們只有一個(gè)異步任務(wù)的時(shí)候使用回調(diào)函數(shù)看起來還不會(huì)有什么問題。 原文地址:http://blog.getify.com/promis... 廈門旅行歸來,繼續(xù)理解Promise 在上一篇深入理解Promise五部曲:1.異步問題中,我們揭示了JS的異步事件輪詢并發(fā)模型并且解釋了多任務(wù)是如何相互穿插使得它們看起來像是同時(shí)運(yùn)行的。...
摘要:有一個(gè)和相關(guān)的更大的問題。最后,請(qǐng)負(fù)有責(zé)任感并且使用安全的擴(kuò)展。深入理解五部曲異步問題深入理解五部曲轉(zhuǎn)換問題深入理解五部曲可靠性問題深入理解五部曲擴(kuò)展性問題深入理解五部曲樂高問題最后,安利下我的個(gè)人博客,歡迎訪問 原文地址:http://blog.getify.com/promis... 現(xiàn)在,我希望你已經(jīng)看過深入理解Promise的前三篇文章了。并且假設(shè)你已經(jīng)完全理解Promises...
摘要:簡(jiǎn)單的說,即將到來的標(biāo)準(zhǔn)指出是一個(gè),所以作為一個(gè),必須可以被子類化。保護(hù)還是子類化這是個(gè)問題我真的希望我能創(chuàng)建一個(gè)忠實(shí)的給及以下。 原文地址:http://blog.getify.com/promis... 如果你需要趕上我們關(guān)于Promise的進(jìn)度,可以看看這個(gè)系列前兩篇文章深入理解Promise五部曲--1.異步問題和深入理解Promise五部曲--2.控制權(quán)轉(zhuǎn)移問題。 Promi...
摘要:一個(gè)就像一個(gè)樂高玩具。問題是不是你小時(shí)候玩兒的那個(gè)有趣,它們不是充滿想象力的打氣筒,也不是一種樂高玩具。這是對(duì)的并不是給開發(fā)者使用的,它們是給庫(kù)作者使用的。不會(huì)超過這兩種情況。第二個(gè)是根據(jù)第一個(gè)處理函數(shù)如何運(yùn)行來自動(dòng)變成狀態(tài)成功或者失敗。 原文地址:http://blog.getify.com/promis... 在 Part4:擴(kuò)展問題 中,我討論了如何擴(kuò)展和抽象Promise是多么...
摘要:只要在調(diào)用異步函數(shù)時(shí)設(shè)置一個(gè)或多個(gè)回調(diào)函數(shù),函數(shù)就會(huì)在完成時(shí)自動(dòng)調(diào)用回調(diào)函數(shù)。要解決的問題是,如何將回調(diào)方法的參數(shù)從回調(diào)方法中傳遞出來,讓它可以像同步函數(shù)的返回結(jié)果一樣,在回調(diào)函數(shù)以外的控制范圍內(nèi),可以傳遞和復(fù)用。 摘要: 我們知道 JavaScript 自從有了 Generator 之后,就有了各種基于 Generator 封裝的協(xié)程。其中 hprose 中封裝的 Promise 和...
閱讀 971·2021-09-26 09:55
閱讀 3239·2021-09-22 15:36
閱讀 3016·2021-09-04 16:48
閱讀 3183·2021-09-01 11:41
閱讀 2620·2019-08-30 13:49
閱讀 1517·2019-08-29 18:46
閱讀 3573·2019-08-29 17:28
閱讀 3467·2019-08-29 14:11