koa源码剖析-co模块以及thunk

Thunk以及CO模块

co4.0之前都是返回的thunk函数
以后的都是返回promise

thunk

thunk:在 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); 
  };
};

临盆环境中,能够运用thunkify将函数转换为thunk 函数

题目:

  1. 为何node 内里大部分的callback都是第一个参数是err呢?

  2. 为何要做thunk 转换呢?

  3. 在redux内里也有thunk middleware,这个thunk是什么意义呢?

CO 模块

co的道理很简单,就是将传入的generator function 转换为一个thunk,而且转换后thunk 的generator的每一个value值作为下一个状况的输入

function co(fn) {

   var isGenFun = isGeneratorFunction(fn);

   return function (done) {//返回的thunk函数,done 作为回调函数
        var ctx = this;
    
        // in toThunk() below we invoke co()
        // with a generator, so optimize for
        // this case
        var gen = fn;
    
        //gen function 转换为generator
        if (isGenFun) {
          var args = slice.call(arguments), len = args.length;
          var hasCallback = len && 'function' == typeof args[len - 1];
          done = hasCallback ? args.pop() : error;
          gen = fn.apply(this, args);
        } else {
          done = done || error;
        }
        //函数实行的时刻就会实行next函数,进入函数体内里
        next();

        // #92
        // wrap the callback in a setImmediate
        // so that any of its errors aren't caught by `co`
        function exit(err, res) {
          setImmediate(done.bind(ctx, err, res));
        }

        function next(err, res) {
          var ret;
    
          // multiple args
          if (arguments.length > 2) res = slice.call(arguments, 1);
    
          // error
          if (err) {
            try {
              ret = gen.throw(err);
            } catch (e) {
              return exit(e);
            }
          }
    
          // ok
          if (!err) {
            try {
              ret = gen.next(res);
            } catch (e) {
              return exit(e);
            }
          }
    
          // done
          if (ret.done) return exit(null, ret.value);
    
          // normalize
          ret.value = toThunk(ret.value, ctx);
    
          // run
          if ('function' == typeof ret.value) {
            var called = false;
            try {
            
              //比方实行yield readFile('test.json'), ret.value就是readFile函数,函数接收一个callback,callback挪用next要领,讲readFile的效果传入了next函数
              ret.value.call(ctx, function(){
                //这里能够防备next函数被屡次实行
                if (called) return;
                called = true;
                next.apply(ctx, arguments);
              });
            } catch (e) {
              setImmediate(function(){
                if (called) return;
                called = true;
                next(e);
              });
            }
            return;
          }
    
          // invalid
          next(new Error('yield a function, promise, generator, array, or object'));
        }
      }
}

经由过程上面的co源码剖析,能够看下面的例子

co(function *() {
    var file = yield readFile('test.json');
    
     //这里的file是经由过程gen.next() 赋值的
    console.log(file);
    
    var ret = yield writeFile(file, 'dest.json');
    
    console.log(ret);    
})

了解了这些基本概念后就能够进入koa的源码阅读了,详细的能够参考下一篇。

    原文作者:qinghui
    原文地址: https://segmentfault.com/a/1190000004592506
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞