Koa基础 thunkify与co如何实现对Generator函数的流程控制

thunkify reposiroty 实现Generator 自动执行

thunkify 是自动执行 Generator 函数的一种方法,出自业界大神TJ。

  • Javascript 的Thunk 函数

在 JavaScript 语言中,Thunk是将多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。

如下面的Demo,

const fs = require('fs');
const path = require('path');
const fpath = path.join(__dirname, 'a.txt');

var Thunk = function(fileName) {
    return function(callBack) {
        return fs.readFile(fileName, callBack);
    }
}
var readFileThunk = Thunk(fpath);
readFileThunk(function(err, data) {
    if (err) console.log(`${err}`);
    else console.log(`${data}`);
})

上面的示例代码中自定义了Thunk 函数,
当var readFileThunk = Thunk(fpath)时,
其实代码返回的是函数
function(callBack) {
return fs.readFile(fileName, callBack);
}

Koa基础 thunkify与co如何实现对Generator函数的流程控制_第1张图片
thunk流程图.jpg

这只是我自己实现的Thunk 函数,我们一起看看npm 第三方库的thunkify

  • thunkify 库核心代码
function thunkify(fn) {

  // thunkify 直接返回就是数组
  return function() {
    // 收集arguments 的参数,保存在临时数组中。
    var args = new Array(arguments.length);
    var ctx = this;

    for (var i = 0; i < args.length; ++i) {
      args[i] = arguments[i];
    }

    return function (done) {
      var called;
      // 将回调函数加载到上一级收集的参数数组的末尾。
      args.push(function () {
        // 确保回掉函数只会被加载一次。
        if (called) return;
        called = true;
        done.apply(null, arguments);
      });

      try {
        fn.apply(ctx, args);
      } catch (err) {
        done(err);
      }
    }
  }
};

var fs = require('fs');
var read = thunkify(fs.readFile);
read('package.json', 'utf8')(function(err, str){
});

与我的Demo在原理上很相似,但是大神的代码严谨性更好,感觉被虐狗了。

  • Thunk 函数有什么作用?

情景:如果我们想从一个文件中读取文件,然后在将读取的内容写入到另一个文件中。读文件是异步的,这时我们要阻塞线程,才能保证存入到新文件的内容是刚才读取的内容。

const fs = require('fs');
const thunkify = require('thunkify');
const path = require('path');
const fpath1 = path.join(__dirname, './test1.txt');
const fpath2 = path.join(__dirname, './test2.txt');

var readFileThunify = thunkify(fs.readFile);
var writeFileThunify = thunkify(fs.writeFile);
var gen = function *() {
    var data = yield readFileThunify(fpath1);
    yield writeFileThunify(fpath2, data);
}

var g = gen();
g.next().value((err, data)  => {
    if (err) console.log(`${err}`);
    else {
        console.log(`${data}`);
        // 将读取到的内容传给写入文件的方法中
        g.next(data).value((err, data) => {
            if (err) console.log(`${err}`);
            else console.log(`${data}`);
        })
    }
})

co repository - 基于Promise自动执行Generator对象

Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way.
官方介绍

  • co 的特点
  1. 回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。
  2. Promise 对象。将异步操作包装成 Promise 对象,用then方法交回执行权。

以下是我通过thunk函数和Promise对象实现的简易generaor对象手动执行器

/**
 * Generator 函数体
 */
const fs = require('fs');
const path = require('path');
var fpath1 = path.join(__dirname, './test1.txt');
var fpath2 = path.join(__dirname, './test2.txt');

function readFileFun(fpath) {
    return new Promise(function(resolve, reject){
        fs.readFile(fpath, function(err, result) {
            if (err) reject(err);
            else resolve(result);
        });
    });
}

var gen = function *() {
    //yield 后是一个promise对象
    var content1 = yield readFileFun(fpath1); 
    var content2 = yield readFileFun(fpath2);
    console.log(`${content1}`); 
    console.log(`${content2}`); 
}
/**
 * manual perform generator.
 */
var g = gen();
g.next().value.then((data) => {
    // 通过next() 函数向generator 函数体传值
    g.next(data).value.then((data1) => {
        g.next(data1);
    })
})

下面是我写的简易自动化执行器

/**
 * automatic perform generator.
 */
var preCO = require('./preCOGenerator');

function run(gen) {    
    var g = gen()
    function next(data) {
        var result = g.next(data);
        // 如果generator 遍历结束,直接返回值。
        if (result.done) return result.value;
        // 通过then() 获取Promise 对象的返回值。
        result.value.then(data => {
            // 递归调用next()
            next(data)
        })
    }
    next();
}

run(preCO.gen);   
  • co 库核心代码
    有兴趣点击co后自行查阅, 原理与我的简易版大同小异,但是菜鸟与大师的差异在于细节。

你可能感兴趣的:(Koa基础 thunkify与co如何实现对Generator函数的流程控制)