前言
這一章的内容學到了事件隊列和異步的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
- 使用計數器,可以保證異步的結果順序
- 使用處理異步調用的工具架構
- 可以防止資料競争
版權聲明
翻譯的文章,版權歸原作者所有,隻用于交流與學習的目的。
原創文章,版權歸作者所有,非商業轉載請注明出處,并保留原文的完整連結。