JavaScript 學習筆記 之 異步(一)

分塊的程序

JavaScript中的程序是由多個塊組成的

程序執行時,只有一個塊是現在執行,其他的塊都是將來再執行

最簡單常見的塊單位是函數

 

我們在寫程序的時候可能會經常遇到一個問題

程序中某個塊我們並不希望在現在執行的塊結束後就立刻執行

比如獲取後臺的數據的塊執行完之後,我們希望當後臺的數據接收完畢之後再執行打印出數據的塊

而不是一發送完請求就立刻打印

 

回調函數是最簡單的實現方式,但不是唯一的實現方式

Ajax也是個很好的實現方式,ajax發送一個異步請求,然後在將來獲取到結果了之後再執行打印數據的塊

(儘可能的不要使用同步Ajax請求,因爲這樣會鎖定瀏覽器UI(按鈕,菜單,滾動條等),並阻塞所有的用戶交互)

 

任何時候只要把一段代碼包裝成一個函數,並指定它在響應某個事件(定時器,鼠標點擊,Ajax響應等)時執行,就等於在代碼中創建了一個將來的塊,也由此引入了異步機制

 

異步控制檯

console.*方法組沒有什麼規範或者需求來指定它們如何工作

控制檯並不是JavaScript的一部分,而是由宿主環境(比如說瀏覽器)添加到JavaScript中的

因此不同的瀏覽器和JavaScript環境可以根據自己的意願來實現

尤其要提出,console.log(..)並不會將傳入的內容立即輸出

因爲I/O是非常低速的阻塞部分,瀏覽器是在後臺異步處理控制檯I/O的

但是大部分情況下並不會有什麼影響

當你發現控制檯輸出的語句並不是你期望的時候

你可能需要考慮下是不是因爲這個原因導致console.log(..)推遲執行了

 

最好的選擇是使用斷點來調試,或者用JSON.stringify(..)進行一次"快照"
 

事件循環

JavaScript引擎並不是獨立運行的,它允許在宿主環境中(如web瀏覽器或者Node.js等)

這些環境都提供了一種機制來處理程序中多個塊的執行,而且執行每塊時調用JavaScript引擎

這種機制就是事件循環

也就是說JavaScript引擎本身沒有時間概念,只是一個按需執行JavaScript代碼的環境

事件調度是由包含它的環境(比如瀏覽器)來進行的

 

那麼,什麼是事件循環

簡單的來說,可以把事件循環看做一個隊列(先進先出)

當一個事件執行完後,從隊列摘下一個事件並執行,這些事件就是你的回調函數

setTimeout(..)定時器的精度可能不是很高,正是因爲setTimeout(..)函數並沒有將回調函數掛在事件隊列中

而是設定了一個定時器,當定時器到時後,環境會把你的回調函數放在事件循環中

當這時事件循環中有很多個事件在排隊等待的時候,這個回調函數得排在其他項目後面

因此執行時間可能會更長

 

事實上,直到ES6,JavaScript才真正內建有直接的異步概念,在此之前都是由宿主環境來管理的

這個改變一個主要原因是,ES6中Promise的引入精確指定了事件循環的工作細節

 

並行線程

異步和並行經常會混爲一談,但事實上它們的意義完全不同

異步是關於現在和將來的時間間隙,而並行是能夠同時發生的事情

 

並行最常見的計算工具就是進程和線程

進程和線程獨立運行,並可能同時運行:在不同的處理器,甚至不同的計算機上

但多個線程能夠共享單個進程的內存

而事件循環不允許對共享內存的並行(同時)訪問和修改

 

完整運行

由於JavaScript的單線程特性

事件中的代碼具有完整性

一旦某個事件開始運行了,這個事件中所有的代碼都會在下一個事件中任意代碼運行之前完成

這稱爲完整運行特性

 

競態條件

JavaScript是單線程編程(不跨線程共享數據),而且有完整運行的特性

這意味着不會出現多線程編程中交錯運行導致的不確定行爲(語句(表達式運算)順序級別的不確定性)

但這不表示JavaScript總是確定性的

比如兩個ajax請求的回調函數的先後執行順序

這種函數的不確定性就被稱爲競態條件(函數(事件)級別的不確定性)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章