前言 這一章的內容學到了事件隊列和非同步的API。js只是運行在其他應用程式的腳本語言。js即依賴於應用程式,也獨立與應用程式。可以使它可以在多平臺,多種環境上運行。ECMAScript標準中沒有關於併發的說明。這章討論的是一些常用的方法,使用事件和非同步API是js編程的基礎部分。非同步API,有set... ...
前言
這一章的內容學到了事件隊列和非同步的API。js只是運行在其他應用程式的腳本語言。js即依賴於應用程式,也獨立與應用程式。可以使它可以在多平臺,多種環境上運行。ECMAScript標準中沒有關於併發的說明。這章討論的是一些常用的方法,使用事件和非同步API是js編程的基礎部分。非同步API,有setTimeout,setInterval。
第61條:不要阻塞I/O事件隊列
個人總結
js是構建在事件之上的單線程語言。js處理交互都以事件的方法進行傳遞的,監聽事件的處理函數,都根據事件隊列的執行相應的監聽函數。
同步處理
同步處理,在一個I/O請求中,會等待輸入的內容。如果沒有輸入會一直等待下去,直到輸入有結果。這個時候,在多線程的語言里可以開另一個線程處理其他計算,但js是單線程的語言,只能一直等,也就是阻塞了,浪費了電腦資源。
非同步處理
在js中,可以為一個I/O操作提供一個回調函數,然後程式會繼續處理下麵的代碼。直到I/O有輸入時,回調函數才會執行相關操作。這是由事件隊列的特性來實現的,這樣就可以實現無阻塞的代碼。
提示
-
非同步API使用回調函數來延緩處理代價高昂的操作以避免阻塞主應用程式
-
js併發地接收事件,但會使用一個事件隊列按序地處理事件處理程式
-
在應用程式事件隊列中絕不要使用阻塞的I/O
第62條:在非同步序列中使用嵌套或命名的回調函數
個人總結
理解操作序列的最簡單的方式是非同步API的發起操作而不是執行操作。所以在發起操作後的代碼會先執行,而到後面的事件迴圈的輪次中,被註冊的事件處理程式才會執行。串聯已完成的非同步操作,可以使用嵌套的方式來進行。但層次過多會導致代碼很亂,很長。減少嵌套的方法:使用命名函數,使用bind方法來綁定。把這些方式結合在一起使用,可以更好解決問題。
提示
-
使用嵌套或命名的回調函數按順序地執行多個非同步操作
-
嘗試在過多的嵌套的回調函數和尷尬的命名的非嵌套回調函數之間取得平衡
-
避免將可被並行執行的操作順序化
第63條:當心丟棄錯誤
個人總結
管理非同步編程,調試不太容易,錯誤發生的地方和錯誤捕獲的地方不好定位。這裡對非同步操作把錯誤的信息的以回調函數參數的形式向外層傳遞。在回調函數中對錯誤進行處理,可以使代碼可以正常運行。
提示
-
通過編寫共用的錯誤處理函數來避免複製和粘貼錯誤處理代碼
-
確保明確地處理所有的錯誤條件以避免丟棄錯誤
第64條:對非同步迴圈使用遞歸
個人總結
非同步迴圈,62條所說的一樣,這裡的迴圈只是同時發起了多個操作,但並不是執行操作。所以無法在執行操作中對迴圈進行中止。把迴圈的操作改寫成函數的遞歸,把非同步的發起和執行進行序列化。但又會有一個新的問題,js環境通常在記憶體中保存一塊固定的區域,稱為調用棧,用於記錄函數調用返回前下一步該做什麼。這是以棧的方式來存儲的“先進後出”。但如果這樣的調用次數過多,會導致棧空間被耗盡,最終會拋出異常,即棧溢出。
提示
-
迴圈不能是非同步的
-
使用遞歸函數在事件迴圈的單獨輪次中執行迭代
-
在事件迴圈的單獨輪次中執行遞歸,並不會導致調用棧溢出
第65條:不要在計算時阻塞事件隊列
個人總結
第61條解釋了非同步API如何防止一段程式阻塞應用程式的事件隊列。如果是一段正常的執行代碼一直占用線程,事件隊列中的操作無法執行。這段時間里在瀏覽器環境中,無法響應
用戶操作。Web客戶端平臺的Worker API,可以處理純數據的計算,從而防止計算時對事件隊列的阻塞。
提示
-
避免在主事件隊列中執行代價高昂的演算法
-
在支持Worker API的平臺,該API可以用來在一個獨立的事件隊列中運行長計算程式
-
在Worker API不可用或代價昂貴的環境中,考慮將計算程式分解到事件迴圈的多個輪次中
第66條:使用計數器來執行並行操作
個人總結
當處理多個併發時,無法保重回調函數中參數的順序。導致後期代碼無法正確運行,對於每次發起操作記一次數,返回時對對應的返回結果進行記錄。回調函數再對記錄的結果進行處理。可以保證程式按預定步驟進行運行。
提示
-
js應用程式中的事件發生是不確定的,即順序是不可預測的
-
使用計數器避免並行操作中的數據競爭
第67條:絕不要同步地調用非同步的回調函數
個人總結
非同步的返回結果,可以保存在一個緩存中。在這種情況下,再進行多文件同步下載,可以使用緩存中的數據,所以回調函數可以同步地執行。但就像64條上的調用棧有可能會出現問題,會導致棧空間耗盡。回調函數也使用非同步調用,使用setTimeout來調用對應的回調函數。
提示
-
即使可以立即得到數據,也絕不要同步地調用非同步回調函數
-
同步地調用非同步的回調函數擾亂了預期的操作序列,並可能導致意想不到的交錯代碼
-
同步地調用非同步的回調函數可能導致棧溢出或錯誤地處理異常
-
使用非同步的API,比如setTimeout函數來調度非同步回調函數,使其運行於另一回合
第68條:使用promise模式清潔非同步邏輯
個人總結
使用promise模式,可以把多層嵌套函數,改寫成一種同步傳入回調函數的方式。這樣可以利用各種工具函數對其進行處理。如then,when,join,select等。
提示
-
promise代表最終值,即並行操作完成時最終產生的結果
-
使用promise組合不同的並行操作
-
使用promise模式的API避免數據競爭
-
在要求有意的競爭條件時使用select(也被稱為choose)
總結
-
非同步調用,利用事件隊列防止阻塞
-
函數的多次遞歸調用,調用棧可能會耗盡
-
純計算,使用web客戶端的Worker API
-
使用計數器,可以保證非同步的結果順序
-
使用處理非同步調用的工具框架
-
可以防止數據競爭