节点中并发活动('map-reduce')的惯用同步?

时间:2016-06-15 11:25:00

标签: javascript node.js ecmascript-6 es6-promise

修改

感谢答案,我有一个工作版本。代码末尾的代码;感谢@estus和@Jared的帮助。

原始问题

以我的方式进入Node,并试图获得并发处理。从一个简单的例子开始:给定两个文件的名称,确定哪个更大。常规(顺序)解决方案:

var fs = require('fs');

var fname1=process.argv[2]
var fname2=process.argv[3]

var stats1 = fs.statSync(fname1)
size1=stats1["size"]

var stats2 = fs.statSync(fname2)
size2=stats2["size"]

if(size1 > size2) {
    console.log(fname1 + " is bigger")
} else if (size2 > size1) {
    console.log(fname2 + " is bigger")
} else {
    console.log("The files are the same size")
}

现在假设我想要并行统计文件*。我可以将代码转换为使用异步stat函数:

var fs = require('fs');

var fname1=process.argv[2]
var fname2=process.argv[3]

fs.stat(fname1, function doneReading(err, stats) {
    size1=stats["size"]
    fs.stat(fname2, function doneReading(err, stats) {
        size2=stats["size"]
        if(size1 > size2) {
            console.log(fname1 + " is bigger")
        } else if (size2 > size1) {
            console.log(fname2 + " is bigger")
        } else {
            console.log("The files are the same size")
        }
    })
})

然而:

  1. 它的可读性较差;
  2. 如果我想比较> 2个文件,它将无法很好地扩展;
  3. 不确定它是否会并行统计文件(我不清楚背景线程的工作原理)。
  4. 所以,具体来说,什么是惯用的方式:

    1. 同时产生多个动作,然后
    2. 一旦完成后使用他们的综合结果?
    3. 也许承诺可能是候选人? Promise.all看起来像等待所有承诺的方式,但不清楚如何实际使用他们的结果。

      感谢。

      'use strict';
      
      const co = require('co');
      const fs = require('fs-promise');
      
      var fname1=process.argv[2]
      var fname2=process.argv[3]
      
      co(function* () {
          let res = yield [fs.stat(fname1), fs.stat(fname2)];
          let size1 = res[0]["size"]
          let size2 = res[1]["size"]
          if(size1 > size2) {
              console.log(fname1 + " is bigger")
          } else if (size2 > size1) {
              console.log(fname2 + " is bigger")
          } else {
              console.log("The files are the same size")
          }
      })
      

      它非常易读,简洁,完全没有回调的肮脏。并且可以随意扩展以比较 n 文件。

      -

      *是的我知道这种情况没有必要这样做;目的是使用一个简单的例子来理解模式。

2 个答案:

答案 0 :(得分:3)

fs.stat(fname1, function doneReading(err, stats) {
    ...
    fs.stat(fname2, function doneReading(err, stats) {
    ...

仍然是连续的而不是平行的,与fs.statSync的区别在于fs.stat是非阻塞的。

建议的可读性'现代节点中的方法是承诺和cofs.stat可能会被宣传(使用pify或Bluebird' Promise.promisify / Promise.promisifyAll)。或者可以使用一些现有的已宣传的fs包,例如fs-promise

以上代码的顺序和非阻塞替代方案可能如下所示:

'use strict';

const co = require('co');
const fs = require('fs-promise');

co(function* () {
    let stat1 = yield fs.stat(fname1);
    let stat2 = yield fs.stat(fname2);
    ...
});

如果我们想让它并行,Promise.all介入:

co(function* () {
    let [stat1, stat2] = yield [fs.stat(fname1), fs.stat(fname2)];
    // a shortcut for
    // let [stat1, stat2] = yield Promise.all([fs.stat(fname1), fs.stat(fname2)]);
    ...
});

答案 1 :(得分:2)

除了estus的优秀答案之外,这可能会更容易理解:

let stat = Promise.promisify(fs.stat.bind(fs));
Promise.all(arrOfPaths.map(stat)).then(arrOfResults => {
  // do stuff
});

如上所述,您需要编写promisify函数或使用添加它的库。

以下是一个示例实现:

const promisify = fn => {
  return function(...args) {
    return new Promise((resolve, reject) => {
      fn.apply(this, [...args, (err, ...rest) => {
        if (err) {
          reject(err);
        }
        let result;
        switch (rest.length) {
          case 0:
            result = true;
            break;
          case 1:
            result = rest[0];
            break;
          default:
            result = rest;
            break;
        }
        resolve(result);
      }]);
    });
  };
};