thunkify源码阅读困惑
发布于 4天前 作者 captainblue2013 204 次浏览 来自 问答
    
/**
 * Module dependencies.
 */

var assert = require('assert');

/**
 * Expose `thunkify()`.
 */

module.exports = thunkify;

/**
 * Wrap a regular callback `fn` as a thunk.
 *
 * @param {Function} fn
 * @return {Function}
 * @api public
 */

function thunkify(fn){
  assert('function' == typeof fn, 'function required');

  return function(){
    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);
      }
    }
  }
};

疑问:

return function(done) 这一行开始,返回一个thunk后的函数, 如果我写的话,就直接 done.apply(null, arguments); 了。。。。

不知道往args里push东西到底什么含义。。。求指点

try-catch这部分也看不明白。。。

10 回复
// fn 原来是这样调用的 fn ( [arg1,] [arg2,] [...] callback ) 
function thunkify(fn){
  assert('function' == typeof fn, 'function required');
  
  // 返回这个函数接收前面的参数  [arg1,] [arg2,] [...] 
  return function(){
    var args = new Array(arguments.length);
    var ctx = this;
    
    // 先把 [arg1,] [arg2,][...] 部分保存起来, 放 args 里
    for(var i = 0; i < args.length; ++i) {
      args[i] = arguments[i];
    }
    // 这里返回的函数接收最后一参数 callback
    return function(done){
     
      // 把传进来的 callback部分 push 进 args里 
      // [arg1,] [arg2,] [...]  callback
      // 这样 args 就变成了 fn 期望接收的参数形式
      // 这用一个called作为标识, 把 callback 包了一下, 保证这个包装过的callback只被执行一次
      var called;
      args.push(function(){
        if (called) return;
        called = true;
        done.apply(null, arguments);
      });

      try {
        // 最终执行的还是原来的函数,  args 也包含了 callback
        fn.apply(ctx, args);
      } catch (err) {
        done(err);
      }
    }
  }
};

就是为了callback只执行一次? 自豪地采用 CNodeJS ionic

@reverland args.push是为了把callback放进args, 把args构造成原来fn所预期的, 最后的参数是callback的形式, 然后下面才可以用args作为参数调用原来的fn, 本来可以写成这样

args.push(done);

而实际的代码把done包了一层, 应该是防止callback执行多次吧

@William17 能不能找个例子说明为什么callback会可能执行多次?

@captainblue2013 我刚接触js不久,不是很清楚。然而记得ydkjs上讲过回调被多次调用是一个问题,把控制权给他的程序并不能知道回调执行了几次什么的。promise设计就只能resolve或者reject一次,保证回调只运行一次。 自豪地采用 CNodeJS ionic

而promise和thunk在使用方式上很相像,ydkjs上还专门做了比较。 自豪地采用 CNodeJS ionic

好像最后什么有用的也没说。。。忽略我╯﹏╰ 自豪地采用 CNodeJS ionic

@captainblue2013 具体怎么想的我也不太清楚, 应该是保证callback即使被不小心错误的调用多次, 结果也只是执行一次 , 例如下面

// 因为程序员的失误 ,next 被调用2次
var middleware=function(req,res,next){
   if( req.auth ){
       req.userInfo={name:"tom"};
       next();
   }
   next();
}

@William17 看到个例子是回调是一个第三方收费系统,一不小心运行了好几次。。。 自豪地采用 CNodeJS ionic

@William17 有道理,虽然写这样代码的人实在是。。。。不过不能不防

回到顶部