【问题标题】:Why is Promise returning (resolve) an empty object in Node.js?为什么 Promise 在 Node.js 中返回(解析)一个空对象?
【发布时间】:2016-12-23 18:06:17
【问题描述】:

我最近在执行 node.js 文件时遇到了问题。 我将发布代码并解释问题所在。

我有 2 个文件,即 testit.js 和 test.js

在 test.js 中,我使用 module.exports 将包含文本文件的文件路径的数组对象传递给 testit.js

["a.txt","b.txt"]

在 testit.js 中,module.exports.text 接受文件名的数组对象,
通过Object.keys(texts).forEach处理每一个,
通过readFile,读取每个返回的缓冲区值,
通过takeAction
返回包含在该缓冲区中的文本 并将其存储在数组对象newtexts中。

但是当 newtexts 被解析并且调用返回到 then() 时,newtexts 正在命令行上打印,它返回一个 EMPTY ARRAY OBJECT 而不是返回每个文件内容的数组对象那些文件。

谁能解释我在代码中哪里出错了? 非常感谢。

test.js

var testit = require('./testit');
var texts = ["a.txt","b.txt"];

testit.text(texts).then(function(newtexts){
  console.log(newtexts);
});

testit.js

var Promise = require('bluebird');
var S = require('string');
var fs = require("fs");

module.exports.text = function(text){

    var texts = text;
    var length = Object.keys(texts).length;

    return new Promise(function(resolve, reject){
        var newtexts = [];

        var takeAction = function(text) {
            return text.toString();
        }

        var readFile = function (filename, enc){
                return new Promise(function (resolve, reject){

                    fs.readFile(filename, enc, function (err, buffer){
                          if(err)
                            reject(err);
                          else
                            resolve(buffer);
                    });
                });
        }

        Object.keys(texts).forEach(function(key){

            readFile(texts[key]).then(function(text){
                 newtexts[key] = takeAction(text);
            });
        });          

        resolve(newtexts);
    });
}

【问题讨论】:

  • 因为您在将任何内容实际添加到新文本之前调用了 resolve(newtexts)。到达 resolve 行时,甚至没有一个 forEach() 回调开始执行。使用 array.map 将您的 texts 数组映射到 Promise 数组,并使用 Promise.all().then() 等待数组中的每个 Promise 完成后再解析 Promise。
  • 我是 Node.js 的新手,如果可能的话,您能否发布一个解决方案?
  • 不是答案,但我发现您在数组上使用 Object.keys 并访问数组 [key] 很奇怪。为什么不直接遍历数组本身?
  • 是的,我本可以使用传统的for 循环而不是使用Object.keys(),而我只是第一次尝试Object.keys(),但两者都无法正常工作@PMV 之前说过。

标签: javascript node.js promise arrayobject


【解决方案1】:

在解决整个 Promise 之前,您实际上需要等待所有 readFile Promise 解决。

替换

    Object.keys(texts).forEach(function(key){

        readFile(texts[key]).then(function(text){
             newtexts[key] = takeAction(text);
        });
    });          

    resolve(newtexts);

这样的:

var textPromises = texts.map( function (fileName) {
    return readFile(fileName).then( function (text) {
        newtexts[fileName] = takeAction(text);
    });
});

Promise.all(textPromises).then( function () {
    resolve(newtexts);
});

这里的基本思想是将每次调用 readFile 返回的 Promise 存储到一个数组中(或者更准确地说,我们存储一个在 readFile 完成并且结果被处理并存储到 newtexts 之后解析的 Promise),并且只当数组中的所有 Promise 都已解决时,我们是否解决了从该函数返回的 Promise。

【讨论】:

    【解决方案2】:

    不必要的承诺值得避免,因为它们是昂贵的操作。您不妨考虑以下问题:

    • 外部new Promise() 包装器是不必要的,因为您可以返回Promise.all(promises)... 返回的承诺。这不仅会消除不必要的承诺,还会允许错误传播给调用者。请注意,在您自己的代码中,永远不会调用外部 Promise 的 reject
    • takeAction() 返回一个新的承诺会降低效率两倍;首先需要创建一个承诺,其次需要另一个 .then() (因此还有另一个承诺)来访问结果。如果操作是同步的,请尽量保持同步。
    • .map() 循环中的.then()s 可以通过将buffer 的同步处理转移到readFile()(适当地重命名)来完全避免。同样,保持同步操作同步。

    试试这个:

    module.exports.text = function (fileNames) {
        var newtexts = {}; // <<<<<<< Object not Array.
    
        function takeAction(key, buffer) { // <<<<<<< takeAction now accepts key and buffer
            newtexts[key] = buffer.toString(); // <<<<<<< make the assignment here
        }
    
        function readFileAndTakeAction(key) {
            return new Promise(function (resolve, reject) {
                fs.readFile(filenames[key], null, function (err, buffer) {
                    if(err) {
                        reject(err);
                    } else {
                        takeAction(key, buffer); // <<<<<<< by doing this here, you avoid an extra .then() elsewhere.
                        resolve();
                    }
                });
            });
        }
        var promises = Object.keys(fileNames).map(readFileAndTakeAction); 
    
        // Instead of resolving an outer promise, return Promise.all(...).then(...) 
        return Promise.all(promises).then(function () {
            return newtexts;
        });
    }
    

    readFile()fs.readFile() 的通用promisifier 更改为专家可以说是剥夺了一些优雅的代码,但肯定会提供更高的效率。此外,.map(readFileAndTakeAction) 的优雅足以弥补。

    【讨论】:

      猜你喜欢
      • 2017-11-11
      • 2020-09-21
      • 2020-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-23
      • 2016-06-04
      • 2021-08-10
      相关资源
      最近更新 更多