小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

co 函數庫的含義和用法

 程序猿130 2019-07-15

進入正文之前,先插播一條消息。

我七年前翻譯的《軟件隨想錄》再版了(京東鏈接)。這次是《Joel論軟件》兩卷同時再版,第一卷是新譯本,第二卷是我翻譯的。

本書的作者是著名程序員、StackOverflow的創(chuàng)始人 Joel Splosky。我覺得,它是軟件項目管理的最好讀物之一,推薦閱讀。

========================================

以下是《深入掌握 ECMAScript 6 異步編程》系列文章的第三篇。

一、什么是 co 函數庫?

co 函數庫是著名程序員 TJ Holowaychuk 于2013年6月發(fā)布的一個小工具,用于 Generator 函數的自動執(zhí)行。

比如,有一個 Generator 函數,用于依次讀取兩個文件。

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

co 函數庫可以讓你不用編寫 Generator 函數的執(zhí)行器。

var co = require('co');
co(gen);

上面代碼中,Generator 函數只要傳入 co 函數,就會自動執(zhí)行。

co 函數返回一個 Promise 對象,因此可以用 then 方法添加回調函數。

co(gen).then(function (){
  console.log('Generator 函數執(zhí)行完成');
})

上面代碼中,等到 Generator 函數執(zhí)行結束,就會輸出一行提示。

二、 co 函數庫的原理

為什么 co 可以自動執(zhí)行 Generator 函數?

前面文章說過,Generator 函數就是一個異步操作的容器。它的自動執(zhí)行需要一種機制,當異步操作有了結果,能夠自動交回執(zhí)行權。

兩種方法可以做到這一點。

(1)回調函數。將異步操作包裝成 Thunk 函數,在回調函數里面交回執(zhí)行權。

(2)Promise 對象。將異步操作包裝成 Promise 對象,用 then 方法交回執(zhí)行權。

co 函數庫其實就是將兩種自動執(zhí)行器(Thunk 函數和 Promise 對象),包裝成一個庫。使用 co 的前提條件是,Generator 函數的 yield 命令后面,只能是 Thunk 函數或 Promise 對象。

上一篇文章已經介紹了基于 Thunk 函數的自動執(zhí)行器。下面來看,基于 Promise 對象的自動執(zhí)行器。這是理解 co 函數庫必須的。

三、基于 Promise 對象的自動執(zhí)行

還是沿用上面的例子。首先,把 fs 模塊的 readFile 方法包裝成一個 Promise 對象。

var fs = require('fs');

var readFile = function (fileName){
  return new Promise(function (resolve, reject){
    fs.readFile(fileName, function(error, data){
      if (error) reject(error);
      resolve(data);
    });
  });
};

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

然后,手動執(zhí)行上面的 Generator 函數。

var g = gen();

g.next().value.then(function(data){
  g.next(data).value.then(function(data){
    g.next(data);
  });
})

手動執(zhí)行其實就是用 then 方法,層層添加回調函數。理解了這一點,就可以寫出一個自動執(zhí)行器。

function run(gen){
  var g = gen();

  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }

  next();
}

run(gen);

上面代碼中,只要 Generator 函數還沒執(zhí)行到最后一步,next 函數就調用自身,以此實現自動執(zhí)行。

四、co 函數庫的源碼

co 就是上面那個自動執(zhí)行器的擴展,它的源碼只有幾十行,非常簡單。

首先,co 函數接受 Generator 函數作為參數,返回一個 Promise 對象。

function co(gen) {
  var ctx = this;

  return new Promise(function(resolve, reject) {
  });
}

在返回的 Promise 對象里面,co 先檢查參數 gen 是否為 Generator 函數。如果是,就執(zhí)行該函數,得到一個內部指針對象;如果不是就返回,并將 Promise 對象的狀態(tài)改為 resolved 。

function co(gen) {
  var ctx = this;

  return new Promise(function(resolve, reject) {
    if (typeof gen === 'function') gen = gen.call(ctx);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);
  });
}

接著,co 將 Generator 函數的內部指針對象的 next 方法,包裝成 onFulefilled 函數。這主要是為了能夠捕捉拋出的錯誤。

function co(gen) {
  var ctx = this;

  return new Promise(function(resolve, reject) {
    if (typeof gen === 'function') gen = gen.call(ctx);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    onFulfilled();
    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }    
  });
}

最后,就是關鍵的 next 函數,它會反復調用自身。

function next(ret) {
  if (ret.done) return resolve(ret.value);
  var value = toPromise.call(ctx, ret.value);
  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) + '"'));
    }
});

上面代碼中,next 函數的內部代碼,一共只有四行命令。

第一行,檢查當前是否為 Generator 函數的最后一步,如果是就返回。

第二行,確保每一步的返回值,是 Promise 對象。

第三行,使用 then 方法,為返回值加上回調函數,然后通過 onFulfilled 函數再次調用 next 函數。

第四行,在參數不符合要求的情況下(參數非 Thunk 函數和 Promise 對象),將 Promise 對象的狀態(tài)改為 rejected,從而終止執(zhí)行。

五、并發(fā)的異步操作

co 支持并發(fā)的異步操作,即允許某些操作同時進行,等到它們全部完成,才進行下一步。

這時,要把并發(fā)的操作都放在數組或對象里面。

// 數組的寫法co(function* () {
  var res = yield [
    Promise.resolve(1),
    Promise.resolve(2)
  ];
  console.log(res); 
}).catch(onerror);// 對象的寫法co(function* () {
  var res = yield {
    1: Promise.resolve(1),
    2: Promise.resolve(2),
  };
  console.log(res); 
}).catch(onerror);

(完)

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多