成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

co模塊用法及分析

muzhuyu / 2725人閱讀

摘要:模塊可以將異步解放成同步。源碼分析使用的模塊版本號(hào)為首先看一些用于判斷對(duì)象類型的函數(shù)對(duì)數(shù)組方法的引用這兩個(gè)應(yīng)該就不用說了吧。。。看一下模塊的輸出部分因此以下三種用法等價(jià)接著就是重頭戲函數(shù)了。

本文只在個(gè)人博客和 SegmentFault 社區(qū)個(gè)人專欄發(fā)表,轉(zhuǎn)載請(qǐng)注明出處
個(gè)人博客: https://zengxiaotao.github.io
SegmentFault 個(gè)人專欄: 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 是一個(gè)輕量級(jí)的框架,本質(zhì)上提供了一個(gè)架子,通過 各種中間件的級(jí)聯(lián)的方式實(shí)現(xiàn)特定的功能。koa 借助 promise 和 generator , 很好解決了異步組合問題。

那什么又是 co 。學(xué)習(xí) koa 就一定少不了學(xué)習(xí) co 模塊。co 模塊可以將異步解放成同步。co 函數(shù)接受一個(gè) generator 函數(shù)作為參數(shù),在函數(shù)內(nèi)部自動(dòng)執(zhí)行 yield 。

co 源碼分析

使用的 co 模塊版本號(hào)為 4.6.0

首先看一些用于判斷對(duì)象類型的函數(shù)

var slice = Array.prototype.slice; // 對(duì)數(shù)組 slice 方法的引用 
function isObject(val) {
  return Object == val.constructor;
}

這兩個(gè)應(yīng)該就不用說了吧。。。

function isPromise(obj) {
  return "function" == typeof obj.then;
}

判斷一個(gè)對(duì)象是否是一個(gè) promise 實(shí)例,判斷的依據(jù)也很簡(jiǎn)單,根據(jù) “鴨子類型”,判斷這個(gè)對(duì)象是否有 then 方法

function isGenerator(obj) {
  return "function" == typeof obj.next && "function" == typeof obj.throw;
}

類似的,判斷一個(gè)對(duì)象時(shí)候是 generator 實(shí)例,只需判斷這個(gè)對(duì)象是否具有 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);
}

判斷是否是一個(gè) generator 函數(shù),只需判斷這個(gè)函數(shù)是否是 GeneratorFunction 函數(shù)的實(shí)例

以上所講的在之后將 value 包裝成 promise 實(shí)例時(shí)都會(huì)用到。

看一下 co 模塊的輸出部分

module.exports = co["default"] = co.co = co

因此以下三種用法等價(jià)

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í)行上下文對(duì)象
  var args = slice.call(arguments, 1) // 傳給 gen 函數(shù)的參數(shù)
  // 返回一個(gè) promise 實(shí)例
  return new Promise(function(resolve, reject) {
       // 根據(jù)傳入的 generator 函數(shù)生成一個(gè) generator 實(shí)例
    if (typeof gen === "function") gen = gen.apply(ctx, args);
    // 如果生成的 gen 不是一個(gè) generator 實(shí)例, 
    // 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);
      }
      // 并將這個(gè)值傳入 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 ,那么這個(gè) promise 的實(shí)例
      // 的狀態(tài)自然變成了 resolved
      if (ret.done) return resolve(ret.value); 
      var value = toPromise.call(ctx, ret.value); // 將 value 重新包裝成一個(gè) promise 實(shí)例
      // 新返回的 promise 實(shí)例的 resolve 方法設(shè)置為 onFulfilled 函數(shù),再次執(zhí)行 next 方法, 從而實(shí)現(xiàn)了自動(dòng)調(diào)用 generator 實(shí)例的 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 模塊就實(shí)現(xiàn)了自動(dòng)執(zhí)行 generator 實(shí)例的 next 方法。那么接下來看看 co 是怎么把一個(gè)值轉(zhuǎn)化為一個(gè) promise 實(shí)例。

function toPromise(obj) {
  if (!obj) return obj;  // 如果傳入的 obj 是假值,返回這個(gè)假值 如 undefined , false, null
  if (isPromise(obj)) return obj; // 如果是 Promise 實(shí)例,返回這個(gè) promise 實(shí)例
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); // 如果是 generator 函數(shù)或者 一個(gè)generator
  if ("function" == typeof obj) return thunkToPromise.call(this, obj); // 如果是 thunk 函數(shù)
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj); // 如果是一個(gè)數(shù)組
  if (isObject(obj)) return objectToPromise.call(this, obj); // 如果是一個(gè) plain object
  return obj; // 如果是原始值,則返回這個(gè)原始值。
}

那么每個(gè)函數(shù)依次看下去。

function thunkToPromise(fn) {
  var ctx = this; // 保存函數(shù)上下文對(duì)象
  // 返回一個(gè) promise 實(shí)例
  return new Promise(function (resolve, reject) {
    // 執(zhí)行傳入的 thunk 函數(shù)
    // thunk 函數(shù)接受一個(gè) 回調(diào)函數(shù) 作為參數(shù)
    fn.call(ctx, function (err, res) {
        // 如果 thunk 函數(shù)運(yùn)行錯(cuò)誤
        // promise 實(shí)例的 變?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 后面是一個(gè) thunk 函數(shù), 這個(gè) thunk 函數(shù)接受一個(gè)回調(diào)參數(shù)作為參數(shù),co 在這個(gè)回調(diào)函數(shù)里定義了何時(shí)將 promise 的狀態(tài)變?yōu)?resolved 或者 rejected ,

function arrayToPromise(obj) {
  // Promise.all 方法返回一個(gè) 新的 promise 實(shí)例
  // 如果 obj 是一個(gè)數(shù)組,把每個(gè)元素包裝成一個(gè) promise 實(shí)例
  // 如果每一個(gè) promise 如果都變?yōu)?resolved 狀態(tài)
  // 那么返回的新的 promise 實(shí)例的狀態(tài)變?yōu)?resloved 狀態(tài)
  // 傳給 resolve 函數(shù)的參數(shù)為之前每個(gè) promise 的返回值所組成的數(shù)組 
  return Promise.all(obj.map(toPromise, this)); 
}

同樣,如果 obj 是一個(gè)數(shù)組,也就是 yield 語句后面的表達(dá)式的值為一個(gè)數(shù)組,那么就執(zhí)行 Promise.all 方法, 將數(shù)組的每一項(xiàng)都變成一個(gè) promise 實(shí)例。

具體方法如下:

使用 toPromise 方法將 obj 數(shù)組中的每一項(xiàng)都包裝成一個(gè) promise 實(shí)例

如果上一步中的數(shù)組中有元素不是 promise 實(shí)例,Promise.all 方法將調(diào)用 Promise.resolve 方法,將其轉(zhuǎn)化為 promise 實(shí)例。

Promise.all 方法返回一個(gè)新的 promise 實(shí)例。

只有 promise 實(shí)例數(shù)組中的所有實(shí)例的狀態(tài)都變?yōu)?resolved 狀態(tài)時(shí),這個(gè)新的 promise 實(shí)例的狀態(tài)才會(huì)變成 resolved。只要數(shù)組中有一個(gè) promise 實(shí)例的狀態(tài)變?yōu)?rejected ,新的promise 實(shí)例狀態(tài)也馬上變?yōu)?rejected 。

當(dāng)返回的新的 promise 實(shí)例狀態(tài)變?yōu)?resolved 時(shí),傳入其 resolve 函數(shù)的參數(shù)為之前數(shù)組中每個(gè) promise 實(shí)例調(diào)用 resolve 函數(shù)的返回值組成的數(shù)組。如果返回的新的 promise 的狀態(tài)變?yōu)?rejected ,那么傳給 reject 函數(shù)的參數(shù)為數(shù)組中的 promise 實(shí)例最先變?yōu)?rejected 狀態(tài)的那一個(gè)執(zhí)行 reject 函數(shù)的返回值。

真繞口,多看幾遍應(yīng)該就能理解了。

最后來看看如果 ret.value 如果是一個(gè)對(duì)象,co 模塊是怎么樣把它變成一個(gè) promise 實(shí)例的。

function objectToPromise(obj){
  // 定義一個(gè)空對(duì)象
  var results = new obj.constructor();
  // 獲取 obj 的全部屬性
  var keys = Object.keys(obj);
  // 用于盛放 每個(gè)屬性值生成的對(duì)應(yīng)的 promise 實(shí)例
  var promises = []; 
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var promise = toPromise.call(this, obj[key]); // 根據(jù)屬性值生成一個(gè) promise 實(shí)例
    if (promise && isPromise(promise)) defer(promise, key); 
    else results[key] = obj[key];
  }
  // 通過一個(gè) promise.all 方法返回一個(gè)新的實(shí)例
  return Promise.all(promises).then(function () {
    return results; // 將 results 作為 onFulfilled 函數(shù)的參數(shù)
  });
  // 函數(shù)的作用
  // 給 promise 添加 resolve 函數(shù)
  // 并且把這個(gè) promise 實(shí)例推入 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 實(shí)例的 resolve 函數(shù)
    }));
  }
}
總結(jié)

分析完 co 的整個(gè)源碼總結(jié)一下整個(gè)執(zhí)行的過程。首先,co 函數(shù)接受一個(gè) generator 函數(shù),并且在 co 函數(shù)內(nèi)部執(zhí)行,生成一個(gè) generator 實(shí)例。調(diào)用 generator 的 next 方法, 對(duì)生成的對(duì)象的 value 屬性值使用 toPromise 方法,生成一個(gè) promise 實(shí)例,當(dāng)這個(gè) promise 實(shí)例的狀態(tài)變?yōu)?resolved 時(shí),執(zhí)行 onFulfilled 方法,再次對(duì) generator 實(shí)例執(zhí)行 next 方法,然后重復(fù)整個(gè)過程。如果出現(xiàn)錯(cuò)誤,則執(zhí)行這個(gè) promise 實(shí)例定義的 reject 函數(shù)即 onRejected 方法。

以上即實(shí)現(xiàn)了將異步過程同步化。

最后歡迎 star

https://github.com/zengxiaotao/zengxiaotao.github.io

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/91339.html

相關(guān)文章

  • co源碼分析其實(shí)踐

    摘要:返回的結(jié)果是一個(gè)對(duì)象,類似于表示本次后面執(zhí)行之后返回的結(jié)果。對(duì)象用于一個(gè)異步操作的最終完成或失敗及其結(jié)果值的表示簡(jiǎn)單點(diǎn)說就是處理異步請(qǐng)求。源碼分析主要脈絡(luò)函數(shù)調(diào)用后,返回一個(gè)實(shí)例。參考鏈接解釋對(duì)象的用法的源碼及其用法 本文始發(fā)于我的個(gè)人博客,如需轉(zhuǎn)載請(qǐng)注明出處。為了更好的閱讀體驗(yàn),可以直接進(jìn)去我的個(gè)人博客看。 前言 知識(shí)儲(chǔ)備 閱讀本文需要對(duì)Generator和Promise有一個(gè)基本的...

    vincent_xyb 評(píng)論0 收藏0
  • 前端每周清單第 10 期:Firefox53、React VR發(fā)布、Microsoft Edge現(xiàn)代

    摘要:新聞熱點(diǎn)國(guó)內(nèi)國(guó)外,前端最新動(dòng)態(tài)發(fā)布近日,正式發(fā)布新版本中提供了一系列的特性與問題修復(fù)。而近日正式發(fā)布,其能夠幫助開發(fā)者快速構(gòu)建應(yīng)用。 前端每周清單第 10 期:Firefox53、React VR發(fā)布、JS測(cè)試技術(shù)概述、Microsoft Edge現(xiàn)代DOM樹構(gòu)建及性能之道 為InfoQ中文站特供稿件,首發(fā)地址為這里;如需轉(zhuǎn)載,請(qǐng)與InfoQ中文站聯(lián)系。從屬于筆者的 Web 前端入門...

    MingjunYang 評(píng)論0 收藏0
  • 前端每周清單半年盤點(diǎn)之 Node.js 篇

    摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。對(duì)該漏洞的綜合評(píng)級(jí)為高危。目前,相關(guān)利用方式已經(jīng)在互聯(lián)網(wǎng)上公開,近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡...

    kid143 評(píng)論0 收藏0
  • 前端每周清單半年盤點(diǎn)之 CSS 篇

    摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。它能夠?yàn)槲覀兲峁╊愃朴陬A(yù)處理器命名空間等多方面的輔助。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(hào)(ID:f...

    RaoMeng 評(píng)論0 收藏0
  • 常用npm模塊分享

    摘要:平時(shí)自己用的模塊也不算少了,其實(shí)網(wǎng)上有很多牛人開發(fā)的模塊都很好,希望不要被埋沒了。一實(shí)用的模塊作用獲取最新可用的迅雷賬號(hào)。用法截圖查看用戶某個(gè)時(shí)間段內(nèi)所有模塊的下載量,按從高到低排名。 平時(shí)自己用的npm模塊也不算少了,其實(shí)網(wǎng)上有很多牛人開發(fā)的npm模塊都很好,希望不要被埋沒了。showImg(http://static.xiaomo.info/images/npm.png); 一、 ...

    sorra 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<