js回調函數以及同步與異步

1. 背景介紹
javascript的單線程特性
因爲javascript語言是一門「單線程」的語言,因此,javascript就像一條流水線,僅僅是一條流水線而已,要麼加工,要麼包裝,不能同時進行多個任務和流程。javascript

任務隊列
單線程就意味着,全部任務須要排隊,前一個任務結束,纔會執行後一個任務。若是前一個任務耗時很長,後一個任務就不得不一直等着。因而就有一個概念——任務隊列。若是排隊是由於計算量大,CPU忙不過來,倒也算了,可是不少時候CPU是閒着的,由於IO設備(輸入輸出設備)很慢(好比Ajax操做從網絡讀取數據),不得不等着結果出來,再往下執行。因而JavaScript語言的設計者意識到,這時主線程徹底能夠無論IO設備,掛起處於等待中的任務,先運行排在後面的任務。等到IO設備返回告終果,再回過頭,把掛起的任務繼續執行下去。html

事件循環
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。 java


2. 知識剖析
名詞解析
那麼這裏說的同步和異步究竟是什麼呢?js官方的文檔在使用這兩個詞的時候並不許確,包括其餘文檔和不少其餘詞彙,都只是聽起來高深,但實際應用好像跟這些詞沒半毛錢關係。例如「路由」這個詞,不知道的人從字面意義上誰又能說出「路由」是什麼意思呢?卻是路由器在生活中常常遇到不會感到陌生,將route使用谷歌翻譯以後,其實就是路徑和線路的意思,這樣路由的概念也就躍然於腦海之中了,也就是說遇到陌生的概念和詞彙,大可沒必要惶恐不安,重要的是理解其背後的本質web

同步和異步的概念
「同步」—— 一下就讓人想到「一塊兒」這個詞;「異步」呢,從字面來說,好像是在不一樣的(異)的ways上do something,那首先想到的詞多是「一邊...一邊...」,好比‘小明一邊吃雪糕一邊寫做業’,這徹底沒毛病,雪糕吃完了,做業也寫完了,這就是異步?這種解釋十分表面,停留在這個層面顯然是不夠的 
不管如何,作事情的時候都是隻有一條流水線(單線程),同步和異步的差異就在於這條流水線上各個流程的執行順序不一樣。 
能夠簡單地理解爲:能夠改變程序正常執行順序的操做就能夠當作是異步操做。例如setTimeout和setInterval函數,Ajax通訊等ajax

同步和異步的區別
同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務,ok,這不難理解;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有等主線程任務執行完畢,"任務隊列"開始通知主線程,請求執行任務,該任務纔會進入主線程執行。網絡

異步運行機制
只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重複。數據結構

全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。
主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
主線程不斷重複上面的第三步。
回調函數
所謂"回調函數"(callback),就是那些會被主線程掛起來的代碼。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應,異步任務必須指定回調函數,當主線程開始執行異步任務,就是執行對應的回調函數。例如ajax的success,complete,error也都指定了各自的回調函數,這些函數就會加入「任務隊列」中,等待執行。異步

3. 常見問題
回調函數,同步,異步,單線程,任務隊列,事件循環,這麼多嚇人的概念,彼此的關聯又是如此的緊密,得想個辦法把這些串起來,並進行總結梳理.async

4. 解決方案
沒法迴避的核心:JavaScript 代碼執行機制
JavaScript是單線程,意味着任務要一個接着一個完成,可是,若是前一個任務執行時間很長,那麼後面的任務就得一直阻塞着,這樣用戶體驗十分差。
JavaScript的設計者考慮到了這一點,因此他將JavaScript的任務分爲兩種,在主線程上執行的任務"同步任務",被主線程掛載起來的任務"異步任務",後者通常是放在一個叫任務隊列的數據結構中
一旦單線程內的全部同步任務執行完畢了,系統就會讀取「任務隊列,這至關於一個while循環,因此也稱爲事件循環
讀取任務隊列中的異步任務所採用的方法就是使用回調函數
5. 編碼實戰
6. 擴展思考
同步和異步在社會生活中的映射
在公路上,汽車一輛接一輛,有條不紊的運行。這時,有一輛車壞掉了。假如它停在原地進行修理,那麼後面的車就會被堵住無法行駛,交通就亂套了。幸虧旁邊有應急車道,能夠把故障車輛推到應急車道修理,而正常的車流不會受到任何影響。等車修好了,再從應急車道回到正常車道便可。惟一的影響就是,應急車道用多了,原來的車輛之間的順序會有點亂。函數

同步能夠保證順序一致,可是容易致使阻塞;異步能夠解決阻塞問題,可是會改變順序性。改變順序性其實也沒有什麼大不了的,只不過讓程序變得稍微難理解了一些。

回調函數(callback)
約會結束後你送你女友回家,離別時,你確定會說:「到家了給我發條信息(call me back),我很擔憂你。」 對不,而後你女友回家之後還真給你發了條信息。小夥子,你有戲了。其實這就是一個回調的過程。你留了個參數函數(要求女友給你發條信息)給你女友,而後你女友回家,回家的動做是主函數。她必須先回到家之後,主函數執行完了,再執行傳進去的函數,而後你就收到一條信息了。

回調,回調,就是回頭調用的意思。主函數的事先幹完,回頭再調用傳進來的那個函數。

7. 參考文獻
參考一:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 
參考二:https://blog.csdn.net/qq_22855325/article/details/72958345

8. 更多討論
問題一 爲何選擇單線程?
JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。

問題二 單線程意味着什麼?
單線程就意味着,全部任務都須要排隊,前一個任務結束,纔會執行後一個任務。若是前一個任務耗時很長,後一個任務就須要一直等着。這就會致使IO操做(耗時但cpu閒置)時形成性能浪費的問題。

問題三 如何解決單線程帶來的性能問題?
答案是異步!主線程徹底能夠無論IO操做,暫時掛起處於等待中的任務,先運行排在後面的任務。等到IO操做返回告終果,再回過頭,把掛起的任務繼續執行下去。因而,全部任務能夠分紅兩種,一種是同步任務(synchronous),另外一種是異步任務(asynchronous)

 

原文:https://blog.csdn.net/web_zyx/article/details/81880702 \

相關文章
相關標籤/搜索