- Node.js+Webpack開發實戰
- 夏磊
- 1835字
- 2021-03-26 21:53:48
3.5 Node.js的異步編程風格
在本書的第1章介紹過Node.js是一個異步運行環境,異步意味著調用函數后結果不是立即返回,而是在未來的時刻再通知給調用者。Node.js使用最廣泛的異步編程風格是基于回調函數來實現的。
截止本書出版時,Node.js可以使用以下幾種異步編程風格:
· 回調函數
· Promise
· async/await
回調函數是最早出現的,也是最煩瑣的,實際應用中不推薦以回調函數的形式進行異步調用。原因是多個異步操作有順序依賴時,會產生如下代碼:

3.5.1 回調函數
Node.js異步編程是通過回調函數來實現的,但不能說使用回調函數就異步了。
如下代碼是基于回調函數的實現,但不是異步的:
function test(callback) { callback(1); } test(function(data) { console.log(data); });
回調函數在完成任務后就會被調用,Node.js使用了大量回調函數,幾乎所有的API都支持回調函數。
由于調用接口存在成功或失敗的情況,而基于回調函數的編程無法使用標準JS中的拋出錯誤和捕獲錯誤的方法。因此只能將錯誤對象作為回調參數來調用回調函數。
Node.js中回調函數的風格是統一的,這樣給我們編程帶來了很大的方便。異步函數的簽名如下:
func(param..., callback(Error, data))
· param調用API的參數。如讀取文件時傳遞的文件路徑,支持多個參數。
· callback(Error, data...)回調函數。Node.js中回調函數的第一個參數永遠是Error對象,之后才是調用成功的結果,如果沒有出錯,第一個參數為null。
例如,我們讀取位于桌面的data.txt文件:

3.5.2 Promise
1.基本知識
Promise對象用于表示一個異步操作的最終完成(或失敗)及其結果值。
一個Promise有以下幾種狀態:
· pending:初始狀態
· fulfilled:操作成功
· rejected:操作失敗
Promise只會從pending轉換為fulfilled或者rejected,整個轉換只發生一次。
Promise構造函數接收一個執行函數,該函數接收resolve和reject兩個回調函數,當執行函數運行成功時需調用resolve,執行錯誤時需調用reject。
Promise構造函數的簽名如下:
function Promise(function(resolve, reject): Promise { // 原來的異步邏輯 });
一旦Promise發生狀態變化,就會觸發then方法,then方法簽名如下:
Promise.prototype.then = function(onFulfilled[, onRejected]): Promise
· onFulfilled Promise:執行成功時回調。
· onRejected Promise:執行出錯時回調,該參數是可選的。
· then方法:返回一個新的Promise對象,因此Promise支持鏈式調用。
由于then的第二個參數onRejected參數是可選的,因此Promise的原型上提供了catch方法來捕獲異步錯誤,catch方法簽名如下:
Promise.prototype.catch = function(onRejected): Promise
· onRejected Promise:執行出錯時回調。
· catch方法:返回一個新的Promise對象。
2.基本使用
Promise是為了解決異步編程問題而出現的,因此可以基于Promise來優化上文中讀取文件的例子:

3.鏈式調用
Promise的then或catch回調函數的返回值會作為下一個then/catch的輸入參數,因此可以通過鏈式Promise來扁平化嵌套的回調函數。

如果需要依次讀取兩個文件,那么就需要嵌套一層回調函數。如果依賴的異步操作越多,響應的嵌套層級也會越大,給代碼的可讀性和可維護性帶來困難。

多個Promise鏈式調用時一旦有一個出錯,整個調用鏈就會終止,然后回調catch函數。
通過鏈式調用,困擾多年的Node.js回調嵌套問題終于得到了第一次解決。
4.其他操作
Promise.resolve(value)
返回一個狀態由value決定的Promise對象。value有以下幾種取值:
· Promise。value本身是Promise的情況下,返回的Promise值由value這個Promise決定。
· 基本類型/空/或不帶then方法的對象。返回的Promise值為value,狀態為fulfilled。
Promise.reject(reason)
返回一個狀態為失敗的Promise,通常情況下會傳遞Error對象作為reason。
Promise.all(promises)
接收一個Promise數組,返回一個新的Promise對象。
· Promise數組中所有Promise都成功執行的情況下,返回的Promise最終會觸發成功。
· Promise數組中只要有一個Promise執行失敗,返回的Promise最終會執行失敗。
Promise.race(promises)
接收一個Promise數組,返回一個新的Promise對象。
當Promise數組中任意一個Promise執行成功或失敗,返回的Promise則立即成功或失敗。
3.5.3 async/await
async和await關鍵字是ES2017中新添加的關鍵字,本質上是Promise的語法糖,使得能夠像同步代碼一樣編寫異步代碼。
async/await本質是語法糖,因此盡管編程風格與同步類似,但是不會阻塞JS線程。
下面使用Promise小節中依次讀取兩個文件的需求為例:

可以看到readFiles函數中的代碼跟同步編程風格一致(忽略async/await關鍵字)。
針對Promise調用鏈太長的問題,async/await提供了一個優美的解決方案,確實讓異步編程變得簡單了。
基本語法
(1)async
async只能放在函數聲明之前,支持普通函數、箭頭函數和類函數。被修飾的函數不管返回什么值,最終都會返回Promise。
· 函數返回基本值/空/或不帶then方法的對象時,Promise的結果為該值,狀態fulfilled。
· 函數拋出錯誤時,Promise狀態為rejected,reason為拋出的錯誤對象。
· 函數本身返回一個Promise時,最終的Promise結果為該Promise的結果。

由于被async修飾的函數最終都會返回Promise,因此需要使用then才可以獲得Promise的執行結果,直接調用函數只能得到一個Promise。
(2)await
await只能在被async修飾的函數內部調用,await可以放在任何返回Promise的函數前,Promise執行成功的情況下,await語句將返回Promise的成功值,Promise執行錯誤的情況下,await語句將拋出錯誤,通過try/catch捕獲即可。
示例:包裝XMLHttpRequest

幾乎所有callback類型的異步函數都可以包裝為Promise(特殊情況就是callback有多個返回值的情況,Promise不能直接處理,但是可以將多個返回值包裝為一個數組)。
- Kali Linux Web Penetration Testing Cookbook
- Python數據分析基礎
- Microsoft Dynamics 365 Extensions Cookbook
- Python編程:從入門到實踐
- 運用后端技術處理業務邏輯(藍橋杯軟件大賽培訓教材-Java方向)
- 青少年信息學競賽
- HTML5秘籍(第2版)
- 小程序,巧應用:微信小程序開發實戰(第2版)
- Extending Unity with Editor Scripting
- 大數據時代的企業升級之道(全3冊)
- Mastering Embedded Linux Programming
- Mastering ASP.NET Core 2.0
- Google Adsense優化實戰
- Learning iOS Penetration Testing
- C語言從入門到精通(微視頻精編版)