重构嵌套回调,node.js,async

Aus*_*Yun 6 asynchronous callback node.js

function indexArticles(callback) {
  fs.readdir("posts/", function(err, files) {
    async.map(files, readPost, function(err, markdown) {
      async.map(markdown, parse, function(err, results) {
        async.sortBy(results, function(obj, callback) {
          callback(err, obj.date);
        }, function(err, sorted) {
          callback( {"articles": sorted.reverse()} );
        });
      });
    });
  });
}
Run Code Online (Sandbox Code Playgroud)

我正试图弄清楚如何使这个更漂亮 - 你可以告诉我我正在使用caolan的异步库,但我不确定使用哪种控制流结构.例如,如果我使用async.waterfall,会产生相当多的代码,每个步骤都必须包含在匿名函数中.例如,这只是带有瀑布的嵌套版本的前两行:

function indexArticles(callback) {
  async.waterfall([
    function(callback) {
      fs.readdir("posts/", function(err, files) {
        callback(err, files)
      })
    },

    function(files, callback) {
      async.map(files, readPost, function(err, markdown) {
        callback(err, markdown)
      })
    }])
}
Run Code Online (Sandbox Code Playgroud)

你会如何改善这一点?

如果有一种方法不仅从左边部分地应用参数,那么我可以看到,例如,

function indexArticles(callback) {
  async.waterfall([
    async.apply(fs.readdir, "posts/"),
    async.apply(async.map, __, readPost),
    async.apply(async.map, __, parse),
    // etc...
  ])
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*ley 6

这是一个有趣的问题,因为您需要将参数绑定到迭代器函数的左侧和右侧,因此,两者bind(也不bindRight是StackOverflow上的一些实现)都不适合您.这里有几个选项:

(1)首先,在你的async.waterfall例子中,你有:

function(callback) {
  fs.readdir("posts/", function(err, files) {
    callback(err, files)
  })
}
Run Code Online (Sandbox Code Playgroud)

这与:

function(callback) {
  fs.readdir("posts/", callback)
}
Run Code Online (Sandbox Code Playgroud)

使用Function.bind和这个方法,您indexArticles可以编写整个函数:

function indexArticles(callback) {
  async.waterfall([
    fs.readdir.bind(this, 'posts/'),
    function(files, cb) { async.map(files, readPost, cb); },
    function(text, cb) { async.map(text, parse, cb); },
    function(results, cb) { async.sortBy(results, function(obj, callback) {
      callback(null, obj.date);
    }, cb) }
  ], function(err, sorted) {
    callback( {"articles": sorted.reverse()} );
  });
};
Run Code Online (Sandbox Code Playgroud)

哪个稍短.

(2)如果你真的想避免包装函数,你可以使用一种部分函数应用程序.首先,在文件的顶部(或在模块中等),定义一个名为的函数partial:

var partial = function(fn) {
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    var currentArg = 0;
    for(var i = 0; i < args.length && currentArg < arguments.length; i++) {
      if (args[i] === undefined)
        args[i] = arguments[currentArg++];
    }
    return fn.apply(this, args);
  };
}
Run Code Online (Sandbox Code Playgroud)

此函数接受函数和任意数量的参数,并undefined在调用函数时将参数列表中的值替换为实际参数.然后你会像这样使用它:

function indexArticles(callback) {
  async.waterfall([
    fs.readdir.bind(this, 'posts/'),
    partial(async.map, undefined, readPost, undefined),
    partial(async.map, undefined, parse, undefined),
    partial(async.sortBy, undefined, function(obj, callback) {
      callback(null, obj.date);
    }, undefined)
  ], function(err, sorted) {
    callback( {"articles": sorted.reverse()} );
  });
}
Run Code Online (Sandbox Code Playgroud)

因此,partial(async.map, undefined, readPost, undefined)返回一个函数,当被Async库调用时fn(files, callback),它会填充files第一个undefined,而callback第二个函数则以undefined调用结束async.map(files, readPost, callback).

(3)还有一个版本的partialFunction.prototype该StackOverflow的答案,让您使用的语法:async.map.partial(undefined, readPost, undefined); 但是,我可能会建议不要这样修改Function.prototype,只是partial作为一个函数使用.

最后,由您决定哪种方法最易读和可维护.