co 原始碼精讀

ZHENGGEGE發表於2017-10-20

原文連結:http://chriscindy.top/post/the-analysis-of-the-source-code-of-co/

co 是著名的 TJ 於 2013 年推出的一個利用 ES6 的 Generator 函式來解決非同步操作的開源專案,也是後來 JavaScript 非同步操作的終極解決方案—— async/await 的先驅。時至今日,co 版本號已經來到了 4.x,不過其程式碼仍然只有寥寥數百行,十分適合閱讀與學習。下面我們就來看一下 co 是如何對非同步操作進行處理的。

首先先來看一下 co 的基本用法。co 使用起來十分方便,只需要將一個 Generator 函式作為引數傳給 co(),就能在該函式中像同步程式碼一樣編寫非同步程式碼。看一下官方示例:

co 原始碼精讀

第 9 行的函式體中,a、b、c 的值都是非同步返回的,但是卻可以像同步一樣呼叫。這便是 co 的魔力。

除此之外,co 還提供了一個 API—— co.wrap() ,用於將被 co 包裹的 generator 函式轉換成為一個返回 promise 的普通函式,示例如下:co 原始碼精讀

瞭解完了 API,我們就可以來看一下 co 內部的實現原理。下面是經過筆者註解(中文註釋)的原始碼:

co 原始碼精讀co 原始碼精讀

co 原始碼精讀co 原始碼精讀

co 原始碼精讀

co 原始碼精讀

我們可以看到,co 的核心邏輯在於第 90 行的 next 函式,這裡將每一次 yield 的返回值包裝成 Promise 物件,在 Promise 的 onFulfilled 和 onRejected 狀態中繼續遞迴呼叫 next 函式,保證鏈式呼叫自動執行,使得非同步的程式碼能夠以同步的方式執行。

可能還有讀者有疑問,如果不借助 Promise,這樣的鏈式自動執行是否還可以實現。事實上,在 co 的 4.0.0 版本以前,其底層實現就沒有藉助 Promise,而是採用了 Thunk 函式的方式。感興趣的讀者可以切換到 3.1.0 版本學習原始碼。

注:如有關於什麼是 Thunk 函式的疑問,點選這裡

阮一峰老師在《ECMAScript 6 入門》一書中對於 Generator 函式自動執行的原理有一個精準的結論:“自動執行的關鍵是,必須有一種機制,自動控制 Generator 函式的流程,接收和交還程式的執行權。回撥函式可以做到這一點,Promise 物件也可以做到這一點。”我們還是以 Promise 物件為例解釋一下這句話:將通過 yield 返回的物件的 value 保持為一個 Promise 物件,執行之,即可拿到程式的執行權。然後通過 Promise.then 和 Promise.reject 方法中呼叫 generator 的 next 方法,可以交還程式執行權。如此達到自動執行 generator 函式的效果。

最後,感謝 co 這樣的優秀專案作為開拓者,才有了後來的 async/await ,讓 JavaScript 開發人員不再因為這門語言獨特的單執行緒特性而深陷非同步程式設計帶來的困擾。


相關文章