co-parallel & co-gather源碼解析

原文連接,轉載請註明出處javascript

最近看了Ma63d關於爬蟲的這篇文章,正好本身也在作爬蟲,看到他在文中提到了co-parallel和co-gather,就打算改一下本身的代碼(原本代碼就只是爲了爬一些本身感興趣的東西,如今還在改,地址在這裏)。昨天也是好好的看了一下co-parallel的源碼,今天打算本身來作一下解析。java

co-parallel

源碼以下:git

var thread = require('co-thread');

module.exports = function *parallel(thunks, n){
  var n = Math.min(n || 5, thunks.length);
  var ret = [];
  var index = 0;

  function *next() {
    var i = index++;
    ret[i] = yield thunks[i];
    if (index < thunks.length) yield next;
  }

  yield thread(next, n);

  return ret;
};

這段代碼真的是很短,可是方法真的很巧妙。由於兩個方法用到了co-thread,這裏把co-thread的源碼也貼出來:github

function thread(fn, n) {
  var gens = [];
  while (n--) gens.push(fn);
  return gens;
}

Run fn n times in parallel.數組

源碼的描述就是爲了parallel執行而建立的。因爲在next外部建立了一個index變量,經過控制index的變化就可使得每次執行的next函數都是不一樣的函數,在next中繼續遞歸yield本身的話就是能夠繼續執行不一樣的next,最終把全部的thunks都yield了一遍,方法是否是很巧妙。函數

若是你以爲我說的話很混亂,那咱們仍是仍是拿一個co-parallel中的例子來說吧ui

var request = require('co-request');
var co = require('co');
var parallel = require('co-parallel');

var urls = [
  'http://google.com',
  'http://yahoo.com',
  'http://ign.com',
  'http://cloudup.com',
  'http://myspace.com',
  'http://facebook.com',
  'http://cuteoverload.com',
  'http://uglyoverload.com',
  'http://segment.io'
];

function *status(url) {
  console.log('GET %s', url);
  var s = (yield request(url)).statusCode;
  return s;
}

co(function *(){
  var start = Date.now();
  var reqs = urls.map(status);
  var res = yield parallel(reqs, 3);
  console.log(res);
  console.log('duration: %dms', Date.now() - start);
});

直接看到var reqs = urls.map(status);這句,因爲傳遞給map的callback是一個Generator函數,因此最後的返回就是Generator的內部指針,也就是Iterator,也就是status()執行了一遍返回的結果。this

圖片描述

再到var res = yield parallel(reqs, 3);這裏,因爲parallel是一個Generator,因此直接進入parallel中,由於n=3因此thread返回的數組內容應該是相似這個 [function*() {yield thunks[0]},function*(){yield thunks[1]}]。因爲yield thread返回的結果是數組,在co中會對數組作Promise.all(obj.map(toPromise, this));由於obj中都是Generator,因此toPromise會直接對每個Generator繼續調用co(function*(){yield thunk[i]})。在next中最後又繼續yield本身,因此噹噹一個thunk結束以後會繼續下一個thunk。google

co-gather

co-gather實現的和co-parallel差很少的功能,只是增長了並行錯誤處理機制。由於Promise.all方法會在其中任何一個出問題的時候都把錯誤扔出來, co-gather是對all中每個方法都作了錯誤處理,讓Promise.all方法不會拋錯,源碼以下:url

var thread = require('co-thread');
module.exports = function *gather(thunks, n){
  n = Math.min(n || 5, thunks.length);
  var ret = [];
  var index = 0;
  function *next() {
    var i = index++;
    ret[i] = {isError: false};
    try {
      ret[i].value = yield thunks[i];
    } catch (err) {
      ret[i].error = err;
      ret[i].isError = true;
    }
    if (index < thunks.length) yield next;
  }
  yield thread(next, n);
  return ret;
};

和co-parallel不一樣的地方就在於對yield thunks[i]作了一層try catch,而後返回的包含執行結果的對象。

總結

單單是看parallel的代碼仍是很好理解的,可是因爲本身co的源碼理解的很差,因此本身在捋清example的時候有點混亂了,後來又從新仔細的看了一遍了co的源碼以及阮一峯的Generator的講解,本身才弄明白。最後再次感謝Ma63d提供的思路。

相關文章
相關標籤/搜索