为什么 Promise 在 Node.js 中返回(解析)一个空对象?

Why is Promise returning (resolve) an empty object in Node.js?

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

我有 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
读取每个返回的缓冲区值 returns 通过 takeAction
该缓冲区中包含的文本 并将其存储在数组对象 newtexts.

但是当新文本被解析并调用 returns 到 then() 时,其中新文本正在命令行上打印,它 returns 一个 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);
    });
}

在解决整个 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 return 存储到一个数组中(或者更准确地说,我们存储一个 Promise,它在 readFile 完成后和结果处理后解析,并且存储到 newtexts 中),并且只有当数组中的所有 Promise 都已解决时,我们才会从这个函数中解决 return 的承诺。

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

  • 外部 new Promise() 包装器是不必要的,因为您可以 return return 由 Promise.all(promises)... 编辑的承诺。这不仅会摆脱不必要的承诺,还会将错误传播给调用者。请注意,在您自己的代码中,永远不会调用外部承诺的 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() 的 general-purpose promisier 更改为 specialist 可以说是剥夺了代码的一些优雅,但肯定会提供更高的效率。此外,.map(readFileAndTakeAction) 的优雅不仅仅是补偿。