晴明的博客园 GitHub      CodePen      CodeWars     

[node] Thunk、co模块

#

Thunk

Thunk函数的定义,它是"传名调用"的一种实现策略,用来替换某个表达式。

#

var thunk = function () {
  return x + 5;
};

function f(thunk){
  return thunk() * 2;
}

 


JavaScript是传值调用。
在JavaScript中,Thunk函数替换的不是表达式,
而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。

#

// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(单参数版本)
var readFileThunk = Thunk(fileName);
readFileThunk(callback);

var Thunk = function (fileName){
  return function (callback){
    return fs.readFile(fileName, callback);
  };
};

 

#自制简单的Thunk函数转换器

var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);

// ES5版本
var Thunk = function(fn){
  return function (){
    var args = Array.prototype.slice.call(arguments);
    return function (callback){
      args.push(callback);
      return fn.apply(this, args);
    }
  };
};

// ES6版本
var Thunk = function(fn) {
  return function (...args) {
    return function (callback) {
      return fn.call(this, ...args, callback);
    }
  };
};

 

#Generator 结合 Thunk 的流程管理

var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

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

var g = gen();//变量g是Generator函数的内部指针,表示目前执行到哪一步。

var r1 = g.next();//next方法负责将指针移动到下一步,并返回该步的信息(value属性和done属性)。
r1.value(function(err, data){
  if (err) throw err;
  var r2 = g.next(data);
  r2.value(function(err, data){
    if (err) throw err;
    g.next(data);
  });
});

 

Thunk函数的自动流程管理

#Generator的自动执行器

var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

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

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }

  next();
}

var gen = function* (){
  var f1 = yield readFile('fileA');//要保证yield后边是thunk函数
  var f2 = yield readFile('fileB');
  // ...
  var fn = yield readFile('fileN');
};

run(gen);//只要执行run函数,这些操作就会自动完成

 

 


co模块

co模块用于Generator函数的自动执行。
可以让你不用编写Generator函数的执行器。
co函数返回一个Promise对象,因此可以用then方法添加回调函数。
co模块其实就是将两种自动执行器(Thunk函数和Promise对象),包装成一个模块。
使用co的前提条件是,Generator函数的yield命令后面,只能是Thunk函数或Promise对象。

co支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步。

#基本用法

var co = require('co');

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

//co(gen);//自动执行
co(gen).then(function (){
  console.log('Generator 函数执行完成');
});

 

#基于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());
};

//手动执行//

var g = gen();

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


//自动执行器//

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);
////



#处理并发的异步操作
把并发的操作都放在数组或对象里面,跟在yield语句后面。

// 数组的写法
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);

 


#三个somethingAsync异步操作,等到它们全部完成,才会进行下一步。

co(function* () {
  var values = [n1, n2, n3];
  yield values.map(somethingAsync);
});

function* somethingAsync(x) {
  // do something async
  return y
}

 

#co.wrap()

//读取文件1
co(function* (){
  var filename = yield readFile('hello1.txt', 'utf-8');
  return filename;
}).then(console.log, console.error);
//读取文件2
co(function* (){
  var filename = yield readFile('hello2.txt', 'utf-8');
  return filename;
}).then(console.log, console.error);

//使用co.wrap()
var getFile = co.wrap(function* (file){
  var filename = yield readFile(file, 'utf-8');
  return filename;
});
getFile('hello.txt').then(console.log);
getFile('hello2.txt').then(console.log);

 

posted @ 2016-06-03 14:35  晴明桑  阅读(342)  评论(0编辑  收藏  举报