国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

走一步再走一步,揭開(kāi)co的神秘面紗

_Dreams / 2851人閱讀

摘要:其實(shí)就是判斷了的屬性是不是個(gè)函數(shù)再接著,如果是個(gè)函數(shù)或者生成器,那就像你自己調(diào)用函數(shù)一樣,手動(dòng)傳到里面去執(zhí)行。

前言

原文地址

源碼地址

了解co的前提是已經(jīng)知曉generator是什么,可以看軟大神的Generator 函數(shù)的語(yǔ)法,
co是TJ大神寫(xiě)的能夠使generator自動(dòng)執(zhí)行的函數(shù)庫(kù),而我們熟知的koa也用到了它管理異步流程控制,將異步任務(wù)書(shū)寫(xiě)同步化,爽的飛起,也擺脫了一直以來(lái)的回調(diào)地獄問(wèn)題。

如何使用

首先我們根據(jù)co的官方文檔來(lái)稍做改變看下,到底如何使用co,再一步步進(jìn)行源碼分析工作(這篇文章分析的co版本是4.6.0)。

yield 后面常見(jiàn)的可以跟的類(lèi)型

promises

array (parallel execution)

objects (parallel execution)

generator functions (delegation)

promises

let co = require("co")
let genTimeoutFun = (delay) => {
  return () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(`delayTime: ${delay}`)
      }, delay)
    })
  }
}
let timeout1 = genTimeoutFun(1000)
let timeout2 = genTimeoutFun(200)

co(function * () {
  let a = yield timeout1()
  console.log(a) // delayTime: 1000
  let b = yield timeout2()
  console.log(b) // delayTime: 200

  return "end"
}).then((res) => {
  console.log(res)
})

array

let co = require("co")
let genTimeoutFun = (delay) => {
  return () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(`delayTime: ${delay}`)
      }, delay)
    })
  }
}
let timeout1 = genTimeoutFun(1000)
let timeout2 = genTimeoutFun(200)

co(function * () {
  let a = yield [timeout1(), timeout2()]
  console.log(a) // [ "delayTime: 1000", "delayTime: 200" ]
  return "end"
}).then((res) => {
  console.log(res) // end
})

objects

let co = require("co")
let genTimeoutFun = (delay) => {
  return () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(`delayTime: ${delay}`)
      }, delay)
    })
  }
}
let timeout1 = genTimeoutFun(1000)
let timeout2 = genTimeoutFun(200)

co(function * () {
  let a = yield {
    timeout1: timeout1(),
    timeout2: timeout2()
  }
  console.log(a) // { timeout1: "delayTime: 1000",timeout2: "delayTime: 200" }
  return "end"
}).then((res) => {
  console.log(res) // end
})

generator functions

let co = require("co")
let genTimeoutFun = (delay) => {
  return () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(`delayTime: ${delay}`)
      }, delay)
    })
  }
}
let timeout1 = genTimeoutFun(1000)
let timeout2 = genTimeoutFun(200)

function * gen () {
  let a = yield timeout1()
  console.log(a) // delayTime: 1000
  let b = yield timeout2()
  console.log(b) // delayTime: 200
}

co(function * () {
  yield gen()

  return "end"
}).then((res) => {
  console.log(res) // end
})

最后說(shuō)一下,關(guān)于執(zhí)行傳入的generator函數(shù)接收參數(shù)的問(wèn)題

let co = require("co")

co(function * (name) {
  console.log(name) // qianlongo
}, "qianlongo")

從co函數(shù)的第二個(gè)參數(shù)開(kāi)始,便是傳入的generator函數(shù)可以接收的實(shí)參

開(kāi)始分析源碼

你可以把以上代碼拷貝至本地測(cè)試一番看看效果,接下來(lái)我們一步步開(kāi)始分析co的源碼

首先經(jīng)過(guò)上面的例子可以發(fā)現(xiàn),co函數(shù)本身接收一個(gè)generator函數(shù),并且co執(zhí)行后返回的是Promise

function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1)

  // we wrap everything in a promise to avoid promise chaining,
  // which leads to memory leak errors.
  // see https://github.com/tj/co/issues/180
  return new Promise(function(resolve, reject) {
    if (typeof gen === "function") gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== "function") return resolve(gen);

    // xxx
  });
}

在Promise的內(nèi)部,先執(zhí)行了外部傳入的gen,執(zhí)行的結(jié)果如果不具備next屬性(且要是一個(gè)函數(shù)),就直接返回,并將執(zhí)行成功回調(diào)resolve(gen),否則得到的是一個(gè)指針對(duì)象。

接下來(lái)繼續(xù)看...

onFulfilled();

/**
  * @param {Mixed} res
  * @return {Promise}
  * @api private
  */

function onFulfilled(res) {
  var ret;
  try {
    ret = gen.next(res); // 用上面執(zhí)行g(shù)en之后的generator生成器將指針指向下一個(gè)位置
  } catch (e) {
    return reject(e);
  }
  next(ret); // 緊接著執(zhí)行next,正是它實(shí)現(xiàn)了反復(fù)調(diào)用自己,自動(dòng)流程控制,注意ret(即上一次gen.next執(zhí)行后返回的對(duì)象{value: xxx, done: true or false})
}

/**
  * @param {Error} err
  * @return {Promise}
  * @api private
  */

function onRejected(err) {
  var ret;
  try {
    ret = gen.throw(err);
  } catch (e) {
    return reject(e);
  }
  next(ret);
}

我覺(jué)得可以把 onFulfilledonRejected 看成是返回的Promise的resolvereject

onFulfilled也是將原生的generator生成器的next方法包裝了一遍,大概是為了抓取錯(cuò)誤吧(看到內(nèi)部的try catch了嗎)

好,我們看到了co內(nèi)部將指針移動(dòng)到了第一個(gè)位置之后,接著執(zhí)行了內(nèi)部的next方法,接下來(lái)聚焦在該函數(shù)上

function next(ret) {
  // 如果整個(gè)generator函數(shù)的內(nèi)部狀態(tài)已經(jīng)表示走完,便將Promise的狀態(tài)設(shè)置為成功狀態(tài),并執(zhí)行resolve
  if (ret.done) return resolve(ret.value);
  // 這一步是將ret的value轉(zhuǎn)換為Promise形式
  var value = toPromise.call(ctx, ret.value);
  // 這里非常關(guān)鍵,是co實(shí)現(xiàn)自己調(diào)用自己,實(shí)現(xiàn)流程自動(dòng)化的關(guān)鍵
  // 注意這里使用value.then,即為返回值添加成功和失敗的回調(diào),在成功的回調(diào)里面再去執(zhí)行onFulfilled,緊接著就是調(diào)用內(nèi)部的next函數(shù)
  // 那不是就保證了流程完全按照你寫(xiě)的順序來(lái)了?
  if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
  // 拋出錯(cuò)誤,yield后只能跟著指定的下列這幾種類(lèi)型
  return onRejected(new TypeError("You may only yield a function, promise, generator, array, or object, "
    + "but the following object was passed: "" + String(ret.value) + """));
}

聰明的你,是不是已經(jīng)明白了co是怎么將異步流程自動(dòng)管理起來(lái)了

但是我對(duì)next函數(shù)中的toPromise函數(shù)還有疑問(wèn),他到底做了什么事?使得co(generatorFun)中yield可以支持?jǐn)?shù)組、對(duì)象、generator函數(shù)等形式。

一步步來(lái)看

function toPromise(obj) {
  // obj不存在,直接返回
  if (!obj) return obj;
  // 如果obj已經(jīng)是Promise,則也是直接返回
  if (isPromise(obj)) return obj;
  // 如果是個(gè)generator函數(shù)或者generator生成器,那就像你自己調(diào)用co函數(shù)一樣,手動(dòng)傳到co里面去執(zhí)行
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  // 如果obj既不是Promise,也不是isGeneratorFunction和isGenerator,要是一個(gè)普通的函數(shù)(需要符合thunk函數(shù)規(guī)范),就將該函數(shù)包裝成Promise的形式
  if ("function" == typeof obj) return thunkToPromise.call(this, obj);
  // 如果是一個(gè)數(shù)組的形式,就去arrayToPromise包裝一番
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;
}

首先如果obj不存在,就直接返回,你想啊,co本來(lái)就是依賴上一次指針?lè)祷氐膙alue是Promise或者其他,這個(gè)時(shí)候如果返回

{
  value: false,
  done: false
}

那就沒(méi)有必要再給一個(gè)false值轉(zhuǎn)成Promise形式了吧。

接著,如果obj本身就是個(gè)Promise也是直接返回,用了內(nèi)部的isPromise函數(shù)進(jìn)行判斷,我們看下他怎么實(shí)現(xiàn)的。

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

其實(shí)就是判斷了obj的then屬性是不是個(gè)函數(shù)

再接著,如果是個(gè)generator函數(shù)或者generator生成器,那就像你自己調(diào)用co函數(shù)一樣,手動(dòng)傳到co里面去執(zhí)行。

isGeneratorFunction

function isGeneratorFunction(obj) {
  var constructor = obj.constructor;
  if (!constructor) return false;
  if ("GeneratorFunction" === constructor.name || "GeneratorFunction" === constructor.displayName) return true;
  return isGenerator(constructor.prototype);
}

通過(guò)obj的constructor屬性去判斷其是否屬于GeneratorFunction,最后如果constructor屬性沒(méi)判斷出來(lái),再去用isGenerator,判斷obj的原型是不是generator生成器

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

判斷的條件也比較直接,需要符合兩個(gè)條件,一個(gè)是obj.next要是一個(gè)函數(shù),一個(gè)是obj.throw要是一個(gè)函數(shù)

接下來(lái)繼續(xù)看

如果obj既不是Promise,也不是isGeneratorFunction和isGenerator,要是一個(gè)普通的函數(shù),就將該函數(shù)包裝成Promise的形式,這里我們主要需要看thunkToPromise

function thunkToPromise(fn) {
  var ctx = this;
  // 將thunk函數(shù)包裝成Promise
  return new Promise(function (resolve, reject) {
      // 執(zhí)行這個(gè)thunk函數(shù)
    fn.call(ctx, function (err, res) { 
      // 注意thunk函數(shù)內(nèi)部接收的回調(diào)函數(shù)中傳入的第一個(gè)參數(shù)是err,出現(xiàn)了err,當(dāng)然需要走reject了
      if (err) return reject(err); 
      // 參數(shù)是兩個(gè)以上的情況下,將參數(shù)整成一個(gè)數(shù)組
      if (arguments.length > 2) res = slice.call(arguments, 1);
      // 最后執(zhí)行成功的回調(diào)
      resolve(res);
    });
  });
}

接下來(lái)是重頭戲了,co中如果處理yield后面跟一個(gè)數(shù)組呢?主要是arrayToPromise函數(shù)的作用

function arrayToPromise(obj) {
  // 使用到了Promise.all,將obj中多個(gè)promise實(shí)例(當(dāng)然你也可以在數(shù)組中填thunk函數(shù),generator函數(shù)等)重新包裝成一個(gè)。最后返回一個(gè)新的Promise
  return Promise.all(obj.map(toPromise, this));
}

還有最后一個(gè)判斷,如果obj是個(gè)對(duì)象怎么辦?

function objectToPromise(obj){
  // 構(gòu)造一個(gè)和傳入對(duì)象有相同構(gòu)造器的對(duì)象, results也是
  var results = new obj.constructor();
  // 獲取obj的keys
  var keys = Object.keys(obj);
  // 存儲(chǔ)obj中是Promise的屬性
  var promises = [];
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var promise = toPromise.call(this, obj[key]);
    // 如果是結(jié)果是Promise,則用defer函數(shù)對(duì)results進(jìn)行修改
    if (promise && isPromise(promise)) defer(promise, key);
    // 如果是非Promise就按原樣返回
    else results[key] = obj[key];
  }
  // 最后 使用到了Promise.all,將obj中多個(gè)promise實(shí)例
  return Promise.all(promises).then(function () {
    return results;
  });

  function defer(promise, key) {
    // predefine the key in the result
    results[key] = undefined;
    promises.push(promise.then(function (res) {
      // 運(yùn)行成功之后再講結(jié)果賦值給results
      results[key] = res;
    }));
  }
}
結(jié)尾

到這里,co源碼分析就告一段落了。總感覺(jué)有些沒(méi)有說(shuō)到位,歡迎大家拍磚,晚安。

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

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

相關(guān)文章

  • JavaScript 異步

    摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫(xiě)一個(gè)符合規(guī)范并可配合使用的寫(xiě)一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開(kāi)發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...

    tuniutech 評(píng)論0 收藏0
  • 你知道koa中間件執(zhí)行原理嗎

    摘要:拿到下一個(gè)中間件后把他交給去處理當(dāng)中間件執(zhí)行結(jié)束了,就把的狀態(tài)設(shè)置為成功。 前言 原文地址 最近幾天花了比較長(zhǎng)的時(shí)間在koa(1)的源碼分析上面,初次看的時(shí)候,被中間件執(zhí)行那段整的暈乎乎的,完全不知道所以,再次看,好像明白了些什么,再反復(fù)看,我去,簡(jiǎn)直神了,簡(jiǎn)直淚流滿面,簡(jiǎn)直喪心病狂啊!!! showImg(https://segmentfault.com/img/remote/146...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<