摘要:模塊可以將異步解放成同步。源碼分析使用的模塊版本號為首先看一些用于判斷對象類型的函數(shù)對數(shù)組方法的引用這兩個應(yīng)該就不用說了吧。。??匆幌履K的輸出部分因此以下三種用法等價接著就是重頭戲函數(shù)了。
寫在前面本文只在個人博客和 SegmentFault 社區(qū)個人專欄發(fā)表,轉(zhuǎn)載請注明出處
個人博客: https://zengxiaotao.github.io
SegmentFault 個人專欄: https://segmentfault.com/blog...
學(xué) nodejs 當(dāng)然避免不了學(xué)習(xí)框架,畢竟原生的 API 較底層。最先接觸的是 Koa ??纯垂倬W(wǎng)的描述
next generation web framework for node.js
我翻譯一下就是: 基于 node.js 的下一代 web 開發(fā)框架。好像很厲害的樣子!koa 是一個輕量級的框架,本質(zhì)上提供了一個架子,通過 各種中間件的級聯(lián)的方式實現(xiàn)特定的功能。koa 借助 promise 和 generator , 很好解決了異步組合問題。
那什么又是 co 。學(xué)習(xí) koa 就一定少不了學(xué)習(xí) co 模塊。co 模塊可以將異步解放成同步。co 函數(shù)接受一個 generator 函數(shù)作為參數(shù),在函數(shù)內(nèi)部自動執(zhí)行 yield 。
co 源碼分析使用的 co 模塊版本號為 4.6.0
首先看一些用于判斷對象類型的函數(shù)
var slice = Array.prototype.slice; // 對數(shù)組 slice 方法的引用
function isObject(val) { return Object == val.constructor; }
這兩個應(yīng)該就不用說了吧。。。
function isPromise(obj) { return "function" == typeof obj.then; }
判斷一個對象是否是一個 promise 實例,判斷的依據(jù)也很簡單,根據(jù) “鴨子類型”,判斷這個對象是否有 then 方法
function isGenerator(obj) { return "function" == typeof obj.next && "function" == typeof obj.throw; }
類似的,判斷一個對象時候是 generator 實例,只需判斷這個對象是否具有 next 方法和 throw 方法。
function isGeneratorFunction(obj) { var constructor = obj.constructor; if (!constructor) return false; if ("GeneratorFunction" === constructor.name || "GeneratorFunction" === constructor.displayName) return true; return isGenerator(constructor.prototype); }
判斷是否是一個 generator 函數(shù),只需判斷這個函數(shù)是否是 GeneratorFunction 函數(shù)的實例
以上所講的在之后將 value 包裝成 promise 實例時都會用到。
看一下 co 模塊的輸出部分
module.exports = co["default"] = co.co = co
因此以下三種用法等價
var co = require("co") // (1) var co = require("co").co // (2) var co = require("co").default // (3)
接著就是重頭戲 co 函數(shù)了。
function co(gen) { var ctx = this; // 保存函數(shù)的執(zhí)行上下文對象 var args = slice.call(arguments, 1) // 傳給 gen 函數(shù)的參數(shù) // 返回一個 promise 實例 return new Promise(function(resolve, reject) { // 根據(jù)傳入的 generator 函數(shù)生成一個 generator 實例 if (typeof gen === "function") gen = gen.apply(ctx, args); // 如果生成的 gen 不是一個 generator 實例, // promise 直接變成 resolved 狀態(tài) if (!gen || typeof gen.next !== "function") return resolve(gen); // 執(zhí)行 onFulfilled 方法 onFulfilled(); function onFulfilled(res) { var ret; try { // 執(zhí)行 gen 的 next 方法 ret = gen.next(res); } catch (e) { return reject(e); } // 并將這個值傳入 next 函數(shù) next(ret); } function onRejected(err) { var ret; try { ret = gen.throw(err); } catch (e) { return reject(e); } next(ret); } function next(ret) { // 如果 gen 執(zhí)行完畢, ret.done 變?yōu)?true ,那么這個 promise 的實例 // 的狀態(tài)自然變成了 resolved if (ret.done) return resolve(ret.value); var value = toPromise.call(ctx, ret.value); // 將 value 重新包裝成一個 promise 實例 // 新返回的 promise 實例的 resolve 方法設(shè)置為 onFulfilled 函數(shù),再次執(zhí)行 next 方法, 從而實現(xiàn)了自動調(diào)用 generator 實例的 next 方法 if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected(new TypeError("You may only yield a function, promise, generator, array, or object, " + "but the following object was passed: "" + String(ret.value) + """)); } }); }
以上,就是 co 模塊就實現(xiàn)了自動執(zhí)行 generator 實例的 next 方法。那么接下來看看 co 是怎么把一個值轉(zhuǎn)化為一個 promise 實例。
function toPromise(obj) { if (!obj) return obj; // 如果傳入的 obj 是假值,返回這個假值 如 undefined , false, null if (isPromise(obj)) return obj; // 如果是 Promise 實例,返回這個 promise 實例 if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); // 如果是 generator 函數(shù)或者 一個generator if ("function" == typeof obj) return thunkToPromise.call(this, obj); // 如果是 thunk 函數(shù) if (Array.isArray(obj)) return arrayToPromise.call(this, obj); // 如果是一個數(shù)組 if (isObject(obj)) return objectToPromise.call(this, obj); // 如果是一個 plain object return obj; // 如果是原始值,則返回這個原始值。 }
那么每個函數(shù)依次看下去。
function thunkToPromise(fn) { var ctx = this; // 保存函數(shù)上下文對象 // 返回一個 promise 實例 return new Promise(function (resolve, reject) { // 執(zhí)行傳入的 thunk 函數(shù) // thunk 函數(shù)接受一個 回調(diào)函數(shù) 作為參數(shù) fn.call(ctx, function (err, res) { // 如果 thunk 函數(shù)運行錯誤 // promise 實例的 變?yōu)?rejected 狀態(tài),執(zhí)行 reject 函數(shù),也就是 co 函數(shù)內(nèi)定義的 onRejected 函數(shù),下同 if (err) return reject(err); // 獲得多余參數(shù) if (arguments.length > 2) res = slice.call(arguments, 1); // promise 狀態(tài)變?yōu)?resolved ,執(zhí)行 resolve 函數(shù),也就是 onFulfilled 函數(shù) resolve(res); }); }); }
所以,總結(jié)一下就是說,如果 generator 里 yield 后面是一個 thunk 函數(shù), 這個 thunk 函數(shù)接受一個回調(diào)參數(shù)作為參數(shù),co 在這個回調(diào)函數(shù)里定義了何時將 promise 的狀態(tài)變?yōu)?resolved 或者 rejected ,
function arrayToPromise(obj) { // Promise.all 方法返回一個 新的 promise 實例 // 如果 obj 是一個數(shù)組,把每個元素包裝成一個 promise 實例 // 如果每一個 promise 如果都變?yōu)?resolved 狀態(tài) // 那么返回的新的 promise 實例的狀態(tài)變?yōu)?resloved 狀態(tài) // 傳給 resolve 函數(shù)的參數(shù)為之前每個 promise 的返回值所組成的數(shù)組 return Promise.all(obj.map(toPromise, this)); }
同樣,如果 obj 是一個數(shù)組,也就是 yield 語句后面的表達式的值為一個數(shù)組,那么就執(zhí)行 Promise.all 方法, 將數(shù)組的每一項都變成一個 promise 實例。
具體方法如下:
使用 toPromise 方法將 obj 數(shù)組中的每一項都包裝成一個 promise 實例
如果上一步中的數(shù)組中有元素不是 promise 實例,Promise.all 方法將調(diào)用 Promise.resolve 方法,將其轉(zhuǎn)化為 promise 實例。
Promise.all 方法返回一個新的 promise 實例。
只有 promise 實例數(shù)組中的所有實例的狀態(tài)都變?yōu)?resolved 狀態(tài)時,這個新的 promise 實例的狀態(tài)才會變成 resolved。只要數(shù)組中有一個 promise 實例的狀態(tài)變?yōu)?rejected ,新的promise 實例狀態(tài)也馬上變?yōu)?rejected 。
當(dāng)返回的新的 promise 實例狀態(tài)變?yōu)?resolved 時,傳入其 resolve 函數(shù)的參數(shù)為之前數(shù)組中每個 promise 實例調(diào)用 resolve 函數(shù)的返回值組成的數(shù)組。如果返回的新的 promise 的狀態(tài)變?yōu)?rejected ,那么傳給 reject 函數(shù)的參數(shù)為數(shù)組中的 promise 實例最先變?yōu)?rejected 狀態(tài)的那一個執(zhí)行 reject 函數(shù)的返回值。
真繞口,多看幾遍應(yīng)該就能理解了。
最后來看看如果 ret.value 如果是一個對象,co 模塊是怎么樣把它變成一個 promise 實例的。
function objectToPromise(obj){ // 定義一個空對象 var results = new obj.constructor(); // 獲取 obj 的全部屬性 var keys = Object.keys(obj); // 用于盛放 每個屬性值生成的對應(yīng)的 promise 實例 var promises = []; for (var i = 0; i < keys.length; i++) { var key = keys[i]; var promise = toPromise.call(this, obj[key]); // 根據(jù)屬性值生成一個 promise 實例 if (promise && isPromise(promise)) defer(promise, key); else results[key] = obj[key]; } // 通過一個 promise.all 方法返回一個新的實例 return Promise.all(promises).then(function () { return results; // 將 results 作為 onFulfilled 函數(shù)的參數(shù) }); // 函數(shù)的作用 // 給 promise 添加 resolve 函數(shù) // 并且把這個 promise 實例推入 promises 數(shù)組 function defer(promise, key) { // predefine the key in the result results[key] = undefined; promises.push(promise.then(function (res) { results[key] = res; // 定義promise 實例的 resolve 函數(shù) })); } }總結(jié)
分析完 co 的整個源碼總結(jié)一下整個執(zhí)行的過程。首先,co 函數(shù)接受一個 generator 函數(shù),并且在 co 函數(shù)內(nèi)部執(zhí)行,生成一個 generator 實例。調(diào)用 generator 的 next 方法, 對生成的對象的 value 屬性值使用 toPromise 方法,生成一個 promise 實例,當(dāng)這個 promise 實例的狀態(tài)變?yōu)?resolved 時,執(zhí)行 onFulfilled 方法,再次對 generator 實例執(zhí)行 next 方法,然后重復(fù)整個過程。如果出現(xiàn)錯誤,則執(zhí)行這個 promise 實例定義的 reject 函數(shù)即 onRejected 方法。
以上即實現(xiàn)了將異步過程同步化。
最后歡迎 star
https://github.com/zengxiaotao/zengxiaotao.github.io
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/91339.html
摘要:返回的結(jié)果是一個對象,類似于表示本次后面執(zhí)行之后返回的結(jié)果。對象用于一個異步操作的最終完成或失敗及其結(jié)果值的表示簡單點說就是處理異步請求。源碼分析主要脈絡(luò)函數(shù)調(diào)用后,返回一個實例。參考鏈接解釋對象的用法的源碼及其用法 本文始發(fā)于我的個人博客,如需轉(zhuǎn)載請注明出處。為了更好的閱讀體驗,可以直接進去我的個人博客看。 前言 知識儲備 閱讀本文需要對Generator和Promise有一個基本的...
摘要:新聞熱點國內(nèi)國外,前端最新動態(tài)發(fā)布近日,正式發(fā)布新版本中提供了一系列的特性與問題修復(fù)。而近日正式發(fā)布,其能夠幫助開發(fā)者快速構(gòu)建應(yīng)用。 前端每周清單第 10 期:Firefox53、React VR發(fā)布、JS測試技術(shù)概述、Microsoft Edge現(xiàn)代DOM樹構(gòu)建及性能之道 為InfoQ中文站特供稿件,首發(fā)地址為這里;如需轉(zhuǎn)載,請與InfoQ中文站聯(lián)系。從屬于筆者的 Web 前端入門...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。對該漏洞的綜合評級為高危。目前,相關(guān)利用方式已經(jīng)在互聯(lián)網(wǎng)上公開,近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。它能夠為我們提供類似于預(yù)處理器命名空間等多方面的輔助。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(ID:f...
閱讀 2600·2021-09-23 11:21
閱讀 1894·2021-09-22 15:15
閱讀 985·2021-09-10 11:27
閱讀 3452·2019-08-30 15:54
閱讀 666·2019-08-30 15:52
閱讀 1343·2019-08-30 15:44
閱讀 2358·2019-08-29 15:06
閱讀 2984·2019-08-28 18:21