【问题标题】:Idiomatic sync of concurrent activities ('map-reduce') in node?节点中并发活动('map-reduce')的惯用同步?
【发布时间】:2016-06-15 11:24:59
【问题描述】:

编辑

感谢回答,我有一个工作版本。问题结束时的代码;感谢@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")
}

现在假设我想并行统计文件*。我可以将代码转换为使用 async 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. 不确定它是否会并行统计文件(我不清楚 atm 后台线程是如何工作的)。

那么,具体来说,惯用的方式是:

  1. 同时产生多个动作,然后
  2. 全部完成后使用它们的组合结果?

也许 promises 可能是一个候选者? 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 个文件。

--

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

【问题讨论】:

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


    【解决方案1】:
    fs.stat(fname1, function doneReading(err, stats) {
        ...
        fs.stat(fname2, function doneReading(err, stats) {
        ...
    

    仍然是顺序的,不是并行的,与fs.statSync的区别在于fs.stat是非阻塞的。

    现代 Node 中建议的“可读”方法是 promise 和 co。 fs.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)]);
        ...
    });
    

    【讨论】:

      【解决方案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);
            }]);
          });
        };
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 2016-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-21
        • 1970-01-01
        相关资源
        最近更新 更多