【问题标题】:Promises.all() on an array of objects with a 'promised' property具有“承诺”属性的对象数组上的 Promises.all()
【发布时间】:2016-02-18 12:58:35
【问题描述】:

编辑:在提出这个问题后的短短几个小时内,我已经学会了足够多的知识来意识到这个问题是一个完美的案例研究,用于严重滥用承诺:

我正在尝试将工作流程转换为使用 Promise,因为它是一个可怕的回调鼠巢,而且我进展顺利,直到我得到一个特定的函数。我正在尝试使用 readFile() 的承诺版本来获取一系列文件的内容,但我还需要保留每个文件的文件名,以便可以将其添加到最终将被解析的对象中下一步/函数中的文件内容。只有在我遍历文件名列表(并创建返回文件内容的承诺)时才能访问此文件名。现在我有这个,这是行不通的:

    // Accepts a sequence of filenames and returns a sequence of objects containing article contents and their filenames
    function getAllFiles(filenames) {
      var files = []; // This will be an array of objects containing Promises on which we want to call .all()
      filenames.each( function (filename) {
        files.push({content: fsPromise.readFileAsync(articlesPath + '/' + filename, 'utf8'), 
                   filename: filename});    
      });

这是试图表达我接下来想做的事情:

      Promise.all(files).then( function(files) {
        return Lazy(files);
      });
    } 

换句话说,我想从 Promise 数组中得到一个对象数组,例如 {content: "FILE CONTENTS", filename: "fileN.txt"}。第一部分是readFileAsync() 的结果,所以这是一个需要解决的承诺,然后我才能继续下一步。据我所知,filenames.each() 调用是我将文件内容与其来源文件的名称相关联的唯一机会。我无法控制文件的内容,所以我不能将文件名存储在文件内容中,这将是丑陋的数据冗余和一个坏主意。 Lazy() 调用只是将完成的数组转换为lazy.js 序列,因为这是我在项目的其余部分中用于收集处理的库。

我正在考虑的半解决方案

是否可以从对象中提取承诺部分并将它们存储在单独的数组中,然后调用.all()?换句话说,当所有的承诺都完成后,我回到原始数组并通过索引检索匹配的文件名?如果我能够用对原始承诺的 references 填充第二个数组(即我知道原始承诺何时解决),我觉得这会起作用。这行得通吗?

或者,

我可以在.each() 调用中进行同步吗?例如。

filenames.each( function(filename) {
  files.push({content: fsPromise.readFileAsync(articlesPath + '/' + filename, 'utf8')
              .then( function(fulfilledContent) {return fulfilledContent}), 
              filename: filename
});

(我知道我还需要处理这些承诺被拒绝而不是解决的可能性——一旦我弄清楚我想要做的事情是否可能,我会想办法做到这一点)

【问题讨论】:

  • 您的最后一个 sn-p 看起来像是语法错误,和/或有缩进问题。我不确定您所说的“同步”是什么意思。
  • 对不起,这只是一个解决方案,而且很可能是语法错误,因为这是我第一次使用 Promise。通过“同步”,我的意思是“等待每个 promise 依次解决,因为 each() 调用迭代文件名数组”——这意味着我最终得到一个完成的对象数组,而不是包含 promise 的对象数组属性。

标签: javascript node.js promise bluebird


【解决方案1】:

有两种简单的解决方案:

  • 在最后一个回调中访问filenames 数组。它与Promise.all 将完成的内容数组的顺序相同:

    Promise.all(filenames.map(function(filename) {
        return fsPromise.readFileAsync(articlesPath + '/' + filename, 'utf8');
    })).then(function(contents) {
        return Lazy(contents).map(function(content, i) {
            return {content: content, filename: filenames[i]};
        });
    })
    
  • 在迭代数组时立即为具有文件名和内容的对象做出承诺:

    Promise.all(filenames.map(function(filename) {
        return fsPromise.readFileAsync(articlesPath + '/' + filename, 'utf8').then(function(content) {
            return {content: content, filename: filename};
        });
    })).then(Lazy);
    

【讨论】:

  • 对,我忘了我实际上可以在执行map() 时获得i 值,我应该知道这一点。不过,我想我更喜欢第二个,谢谢。
  • 好吧,很抱歉再次提出这个问题,但我认为会是一个小改动完全让我感到难过——如果我希望初始文件名数组也成为一个承诺,我该怎么办?您的解决方案在数组上​​完美运行,但我仍然在概念上苦苦挣扎并退回到回调样式模式,甚至它们对我不起作用......我应该使用 async.js 而不是 bluebird 以便我d 至少有适当的文档可供阅读?也许我会改为阅读异步文档,看看我可以在这里应用什么......
  • 一组文件名承诺?用Promise.all 打它。一个数组的承诺?然后您需要将我的答案中的代码放在then 回调中(并可能应用some flattening)。
  • 我认为“扁平化”是让我伤心的部分,我会再试一次,谢谢
猜你喜欢
  • 2018-12-11
  • 2019-01-05
  • 2017-12-14
  • 2021-12-27
  • 1970-01-01
  • 2016-01-05
  • 1970-01-01
  • 2020-11-18
相关资源
最近更新 更多