【问题标题】:Q promises not working in lodash reduce functionQ 承诺不能在 lodash reduce 函数中工作
【发布时间】:2014-05-12 04:46:36
【问题描述】:

我正在尝试将文件返回到具有最早修改日期的目录中。这种方法在 createFileDateMap 函数中似乎失败了。我想减少一组文件路径并使用文件名和修改日期创建一个对象。 getModDate 函数是一个异步 fs.lstat 调用。我似乎无法将减速器的 acc 设置为 .then() 块中的值。当值依赖于异步调用时,我不确定如何实现减少

var _ = require('lodash'),
    fs = require('fs'),
    path = require('path'),
    Q = require('q');

function checkDir(dir) {
    // Check if given path is valid directory
    var deferred = Q.defer();
    fs.lstat(dir, deferred.makeNodeResolver());
    return deferred.promise.invoke('isDirectory');
}

function getFiles(dir) {
    // Get all files within a directory
    var deferred = Q.defer();
    fs.readdir(dir, deferred.makeNodeResolver());
    return deferred.promise;
}

function makeFullPathFileArr(dir, files) {
    // Return array of full paths
    return _.map(files, function(file) {
        return path.join(dir, file);
    });
}

function getModDate(file) {
    // Return modification date of file
    var deferred = Q.defer();
    fs.stat(file, deferred.makeNodeResolver());
    return deferred.promise.get('mtime');
}

function createFileDateMap(filesArr) {
    // Return an obj of file paths and modification dates as Date objects
    // {{ file1: Date, file2: Date }}
    var fileDateMap = _.reduce(filesArr, function(acc, file) {
        getModDate(file)
            .then(function(modDate) {
                acc[file] = moment(modDate);
            });
        return acc;
    }, {});
    return fileDateMap;
}

function getMinDateFile(mapObj) {
    // return the file name which has the earliest modification date
    var dates = _.transform(mapObj, function(result, date, key) {
        result[key] = new Date(date);
    });
    var minDate = new Date(Math.min.apply(null, _.values(dates)));
    var invertedMapObj = _.invert(mapObj);

    return invertedMapObj[minDate];
}

var dir = '../reports';
checkDir(dir)
    .then(function(exist) {
        if(exist) {
            getFiles(dir)
                .then(function(fileNames) {
                    return makeFullPathFileArr(dir, fileNames);
                })
                .then(function(fullpathsArr) {
                    return createFileDateMap(fullpathsArr);
                })
                .then(function(fileAndDatesObj) {
                    console.log(getMinDateFile(fileAndDatesObj));
                });
        }
    })
    .catch(function(err) {
        console.log(err);
    });

【问题讨论】:

  • 顺便说一句,看看Q.ninvoke()Q.nfbind()
  • 你没有从 reduce 函数返回。那是你的错误。此外,正确地承诺您的 API(如 SLaks 建议的)可以真正帮助您编写代码。
  • 我正在浏览 Q 文档。我将如何使用这些方法?我认为在这里使用 makeNodeResolver() 更合适。
  • 在您最近的编辑之后,您的退货声明仍然是错误的。您需要从减少返回,而不是从.then。您确定您不只是在寻找等待一系列承诺全部解决的Q.all 吗? Q.all(filesArr.map(getModDate)).then(function(files){...
  • @Ptrkcon 请不要将答案编辑到问题中,将问题保留在初始修订版中 - 否则作为问题毫无意义。至于手动承诺 API,风险更大,因为承诺是安全抛出的(它们的一大优势)并且具有合理的异常处理 - 这要求返回承诺的函数也不会同步抛出(而是返回被拒绝的承诺) - 很多时候人们在将回调 API 转换为 Promise 时会错过更微妙的地方,尤其是对于延迟对象。

标签: javascript node.js promise q lodash


【解决方案1】:

感谢@BenjaminGruenbaum 的帮助。 :)

function createFileDateMap(filesArr) {
    // Return an obj of file paths and modification dates as Date objects
    // {{ file1: Date, file2: Date

    return Q.all(_.map(filesArr, getModDate))
        .then(function(modDates) {
           return _.zipObject(filesArr, modDates);
        });
}

【讨论】:

    【解决方案2】:

    问题在于reduce 是同步的,并且不知道您在其回调中使用的承诺。您将返回 fileDateMap,它是一个空对象。这个问题有两种解决方案:

    • 并行调用所有 getModDates,使用 Q.all 获取日期数组的承诺,并在 then 回调中减少它。

      function createFileDateMap(filesArr) {
          // Return an obj of file paths and modification dates as Date objects
          // {{ file1: Date, file2: Date }}
          return Q.all(_.map(filesArr, function(file) {
              return getModDate(file).then(function(date) {
                  return {name:file, date:date};
              });
          })).then(function(fileAndDateArr) {
              return _.reduce(fileAndDateArr, function(acc, o) {
                  acc[o.file] = moment(o.date);
                  return acc;
              }, {});
          });
      }
      
    • 使accumlutor 成为地图对象的承诺,并通过链接大量then 调用来“异步”减少数组。

      function createFileDateMap(filesArr) {
          // Return an obj of file paths and modification dates as Date objects
          // {{ file1: Date, file2: Date }}
          return _.reduce(filesArr, function(acc, file) {
              return acc.then(function(fileDateMap) {
                  return getModDate(file).then(function(modDate) {
                      fileDateMap[file] = moment(modDate);
                      return fileDateMap;
                  });
              });
          }, Q({}));
      }
      

    【讨论】:

    • 您对 _.zipObject 的感觉如何,它本质上是在做您的顶级功能正在做的事情?有什么缺点吗?
    • 它会更容易阅读并且可能更高效 :-) 我的函数只是有点 cleaner 更纯粹,因为filesArr 无关紧要和modDates 的顺序相同,因为它将文件名+日期存储在一个对象中。了解我的解决方案,然后使用你的 sn-p :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-12
    • 2017-10-26
    • 2016-09-05
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    • 2015-02-25
    相关资源
    最近更新 更多