【问题标题】:Promise.all() resolving, but it shouldn't (node.js)Promise.all() 解决,但不应该(node.js)
【发布时间】:2016-04-22 20:52:12
【问题描述】:

我现在正在 node.js 中尝试这样的事情:

var exec = require('child_process').exec
var write = require('fs-writefile-promise')

function run() {
    var myArray = [];   
    var execs = [];

    for (var i = 1; i <= 7; i++) {
        (function(cntr) {
            write('file-' + i + '.txt', someString)
            .then(function (filename) {
                execs.push(new Promise(function(resolve, reject) {
                    exec('cat ' + 'file-' + cntr + '.cnf', function(error, stdout, stderr) {
                        console.log(cntr + ' ' + stdout);
                        if (stdout.search(/\bsomeString\b/) > -1) {
                            myArray.push(cntr);
                            resolve();
                        } 
                        else {
                            resolve();
                        }   
                    })
                }))
            })
            .catch(function (err) {
                console.error(err);
            });
        })(i);
    }
    return Promise.all(execs).then(function() {
        return new Promise(function(resolve) {
            resolve(myArray);
        }) 
    })
}

run().then(function(result) {
    console.log(result);
});

如您所见,我正在创建多个运行 exec() 的 Promise,并且每个 Promise 都会在 exec() 完成时解析。 然后我正在等待Promise.all(execs) 中的每个 Promise 解决,以将 myArray 作为 Promise 返回。然而,当我最后执行 run() 函数时,它返回一个空数组。我想这与Promise.all() 有关,因为即使execs 中的一些Promises 尚未解决,它也会解决,但我不确定,这就是为什么我真的需要一些帮助。有谁知道我在哪里犯了代码错误?

非常感谢您!

#EDIT 1

var exec = require('child_process').exec
var write = require('fs-writefile-promise')

function run() {
    var myArray = [];   
    var execs = [];

    for (var i = 1; i <= 7; i++) {
        (function(cntr) {
            return new Promise(function(resolve, reject) {
                fs.writeFile('file-' + i + '.txt', someString, (err) => {
                    if (err) {
                        reject();
                    }
                    else {
                        resolve();
                    }
                });
            })
            .then(function (filename) {
                execs.push(new Promise(function(resolve, reject) {
                    exec('cat ' + 'file-' + cntr + '.cnf', function(error, stdout, stderr) {
                        console.log(cntr + ' ' + stdout);
                        if (stdout.search(/\bsomeString\b/) > -1) {
                            myArray.push(cntr);
                            resolve();
                        } 
                        else {
                            resolve();
                        }   
                    })
                }))
            })
            .catch(function (err) {
                console.error(err);
            });
        })(i);
    }
    return Promise.all(execs).then(function() {
        return new Promise(function(resolve) {
            resolve(myArray);
        }) 
    })
}

run().then(function(result) {
    console.log(result);
});

【问题讨论】:

  • 代码被 promise 构造函数反模式所困扰。你需要了解异步代码是如何工作的,什么时候发生,然后再试一次。
  • 您正在异步推送到execs,并且在调用Promise.all 时数组始终为空。立即将值的承诺推送到该数组。
  • @Bergi 立即推送承诺是什么意思?
  • .then() 中的任何函数运行稍后,这意味着你的execs.push 发生在之后 Promise.all(execs),这意味着你正在做Promise.all([]) 有效。正如 Bergi 所说,将“写入”承诺推送到 execs 数组(实际上是整个承诺链)上。现在,您将循环中的这 7 个承诺返回到无处。

标签: javascript node.js asynchronous promise


【解决方案1】:

您的两次尝试都存在许多问题。第一次尝试的问题是您在异步操作之后填充 execs 数组,因此当您将数组实际传递给 Promise.all() 时,其中没有任何内容,因此 Promise.all() 无需等待。

此外,您不只是使用已经创建的 Promise,因此您最终会做出比需要更多的 Promise。

一般来说,最好在主逻辑之外“承诺”您的异步操作,然后让您的所有逻辑都由承诺驱动,而不是将承诺与普通回调混合和匹配。以下是试图解决这些问题的版本:

var exec = require('child_process').exec
var write = require('fs-writefile-promise')

// make promisified version of exec
function execP(file, options) {
    return new Promise(function(resolve, reject) {
        exec(file, options, function(err, stdout, stderr) {
            if (err) return resolve(err);
            resolve({stdout: stdout, stderr: stderr});
        });
    });
}

function run() {
    var promises = [];
    for (var i = 1; i <= 7; i++) {
        promises.push(write('file-' + i + '.txt', someString).then(function(filename) {
            return execP(filename);
        }));
    }
    return Promise.all(promises).then(function(results) {
        // results is an array of {stdout: xxx, stderr: yyy} objects
        // process those results into a new array of just indexes
        var final = [];
        results.forEach(function(data, index) {
            if (data.stdout.search(/\bsomeString\b/) > -1) {
                final.push(index);
            }
        });
        return final;
    });
}

run().then(function(results) {
    // array of indexes that contained the desired search string
}, function(err) {
    // process error here
});

注意:这将并行运行您的所有 exec 操作,这就是您的原始代码所做的。如果您想按顺序运行它们,也可以这样做,但需要进行一些调整。

【讨论】:

    【解决方案2】:

    由于write 是异步的,因此该阶段的程序正在传递回主线程,这会直接传递到您的 promise.all 并在加载 exec 之前返回。

    我建议您创建一个函数,该函数返回 both 一个文件保存后跟 exec 的承诺。

    【讨论】:

    • 感谢您的回答。我编辑了我的代码,您可以在我的原始帖子的 Edit 1 中看到。我用一个新的 Promise 替换了 write() 函数,我在其中写入了文件。然而,这仍然不起作用,但它应该与文档有关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-09
    • 2020-06-23
    • 1970-01-01
    • 2015-08-25
    • 1970-01-01
    • 2015-02-10
    • 2019-03-11
    相关资源
    最近更新 更多