原文連接:http://chriscindy.top/post/the-analysis-of-the-source-code-of-co/html
co 是著名的 TJ 於 2013 年推出的一個利用 ES6 的 Generator 函數來解決異步操做的開源項目,也是後來 JavaScript 異步操做的終極解決方案—— async/await 的先驅。時至今日,co 版本號已經來到了 4.x,不過其代碼仍然只有寥寥數百行,十分適合閱讀與學習。下面咱們就來看一下 co 是如何對異步操做進行處理的。
首先先來看一下 co 的基本用法。co 使用起來十分方便,只須要將一個 Generator 函數做爲參數傳給 co(),就能在該函數中像同步代碼同樣編寫異步代碼。看一下官方示例:
git
第 9 行的函數體中,a、b、c 的值都是異步返回的,可是卻能夠像同步同樣調用。這即是 co 的魔力。github
除此以外,co 還提供了一個 API—— co.wrap()
,用於將被 co 包裹的 generator 函數轉換成爲一個返回 promise 的普通函數,示例以下:編程
瞭解完了 API,咱們就能夠來看一下 co 內部的實現原理。下面是通過筆者註解(中文註釋)的源碼:
promise
咱們能夠看到,co 的核心邏輯在於第 90 行的 next 函數,這裏將每一次 yield 的返回值包裝成 Promise 對象,在 Promise 的 onFulfilled 和 onRejected 狀態中繼續遞歸調用 next 函數,保證鏈式調用自動執行,使得異步的代碼可以以同步的方式運行。異步
可能還有讀者有疑問,若是不借助 Promise,這樣的鏈式自動執行是否還能夠實現。事實上,在 co 的 4.0.0 版本之前,其底層實現就沒有藉助 Promise,而是採用了 Thunk 函數的方式。感興趣的讀者能夠切換到 3.1.0 版本學習源碼。async
注:若有關於什麼是 Thunk 函數的疑問,點擊這裏。異步編程
阮一峯老師在《ECMAScript 6 入門》一書中對於 Generator 函數自動執行的原理有一個精準的結論:「自動執行的關鍵是,必須有一種機制,自動控制 Generator 函數的流程,接收和交還程序的執行權。回調函數能夠作到這一點,Promise 對象也能夠作到這一點。」咱們仍是以 Promise 對象爲例解釋一下這句話:將經過 yield 返回的對象的 value 保持爲一個 Promise 對象,執行之,便可拿到程序的執行權。而後經過 Promise.then 和 Promise.reject 方法中調用 generator 的 next 方法,能夠交還程序執行權。如此達到自動執行 generator 函數的效果。函數
最後,感謝 co 這樣的優秀項目做爲開拓者,纔有了後來的 async/await ,讓 JavaScript 開發人員再也不由於這門語言獨特的單線程特性而深陷異步編程帶來的困擾。post