用Node.js編寫複雜控制流的解決之道:async庫+大量使用高階函數和柯里化。
发布于 8 年前 作者 byvoid 8530 次浏览 最后一次编辑是 4 年前

async是一個非常好用的模塊,祗是如果沒有高階函數的使用,用起來代碼還是相當混亂。下面是我寫的一個簡單的柯里化(部分求值)函數:

Function.prototype.curry = function () {
  var appliedArgs = arguments;
  var self = this;
  return function () {
    var j = 0;
    var args = [];
    for (var i = 0; i < appliedArgs.length; i++) {
      if (appliedArgs[i] === undefined) {
        appliedArgs[i] = arguments[j];
        j ++;
      }
      args.push(appliedArgs[i]);
    }
    for (; j < arguments.length; j++) {
      args.push(arguments[j]);
    }
    return self.apply(this, args);
  };
}

例如函數:

function func(a, b, c, d, e) {
 //do something
}

var f1 = func.curry('a', 'b', 'c');
f1('d', 'e')

var f2 = func.curry(undefined, 'b', 'c', undefined, 'e');
f2('a', 'd')

以後可以把控制流所有的部分抽象爲高階函數,然後通過柯里化結合async的parallel, series, waterfall等方法,即可寫出清晰可讀的複雜控制流。

23 回复

啊?难道你不知道Jscex吗?要清晰可读怎么会比得过Jscex……不信你说一段逻辑,我们来比一下,哈哈。

當然知道jscex啊,也用過,但就是不喜歡加一層編譯。

@byvoid 萝卜白菜,各有所爱

类似这种 “复杂” “嵌套” “逻辑” 的帖子,不用点开都能猜到老赵 @jeffz 和 jscex 肯定在里面

@byvoid 要克服没啥道理的心理障碍……

太了解我了!

@jeffz jscex不能並行執行邏輯

啊?难道你不知道 Moescript 吗?要青紫刻度怎么会比得过 Moescript……不信你说一段逻辑,我们来比一下,哈哈。

(跟风莫喷)

@byvoid 要并行还是串行完全看你怎么写,比如你可以了解下whenAll是做什么的,而且即便没有whenAll也可以很轻松的把需要的逻辑并行起来,随便找个Jscex介绍就会提到的。Jscex是很有背景的研究成果的JS实现,经过反复验证过的手段,不存在这些低级问题……

楼主的思路不错。

function incrAB(callback) {
  async.parallel([
    function(_callback) {
      redis.incr('a', _callback)
    }),
    function(_callback) {
      redis.incr('b', _callback)
    })
  ], callback);
};
function incrAB(callback) {
  async.parallel([
    redis.incr.curry(redis, 'a'),
    redis.incr.curry(redis, 'b')
  ], callback)
};



@jeffz 哈哈,老赵的Jscex很不错,不过有时候在Task里的$await没法被识别,可能是if嵌套多了,不过可以用task.start()代替。

@xieren58if套多了完全没关系的,我也不知道怎么用task.start()代替,如果你遇到这个问题说明是bug,请去github上发一个issue。

@jeffz 已经发issue,不知道是不是我用法不对~~

@jeffz 回复很快呀,感谢老赵~

+1 就是這種方法。此外還可以增加其他高階函數的方法,譬如修改回調函數的參數個數,傳遞多餘的參數等等

@byvoid 唉,我們都2了,ES5的bind方法就可以啊

function incrAB(callback) {
  async.parallel([
    redis.incr.bind(redis, 'a'),
    redis.incr.bind(redis, 'b')
  ], callback)
};

@guilin bind做不到綁定非第一個參數,這就是我爲什麼實現curry

@guilin 非前N個參數,例如我要綁定第一個和第三個,空出第二個和第四個。

@byvoid 是的, 不过感觉这种情况不多啊

@guilin 我使用很多的

给出的下面的例子中 incrAB 这个方法,为什么不用定义_callback 这个参数了呢?我觉得每次传_callback也挺麻烦的,你是怎么省略的?

@guilin redis.incr.curry(redis, 'a'), 这个语句如何做的省略_callback?

回到顶部