Node.js中then()函数在Promise resolve之前执行

4
我有一个使用Promise的问题,需要实现以下功能:从stdout获取文件名,将其拆分成行并进行复制。当复制操作完成后,我想要开始其他操作,但这就是我的问题。
我在Promise内部创建了一个复制函数,在出现错误时会立即拒绝它,如果没有错误,我会在完成循环中的复制后解决它,但由于某种原因,then()内部的函数在复制操作完成之前就被执行了。
var lines  = stdout.split(/\r?\n/);

copyUpdatedFiles(lines).then(
    function() {
       console.log('this one should be executed after copy operation');
    }
);

function copyUpdatedFiles(lines) {
    return new Promise(function(resolve, reject) {
        for (var i = 0; i < linesLength; i++) {
            fs.copy(lines[i], target, function(err) {
                if (err) {
                    reject();
                }
            });
        }
        resolve();
    });
}

请帮忙,因为我显然漏掉了什么重要的东西。

2个回答

4

在你调用resolve之后,它会立即被解决,这是在复制开始但尚未完成之前完成的。在你 resolve 之前必须等待最后一个回调。这意味着要跟踪你看到了多少,看到***注释:

function copyUpdatedFiles(lines) {
    return new Promise(function(resolve, reject) {
        var callbacks = 0;                              // ***
        for (var i = 0; i < linesLength; i++) {
            fs.copy(lines[i], target, function(err) {
                if (err) {
                    reject();
                } else {                                // ***
                    if (++callbacks == lines.length) {  // ***
                        resolve();                      // ***
                    }                                   // ***
                }                                       // ***
            });
        }
    });
}

此外,有一些库可以将 NodeJS 风格的回调函数转换为 Promise 形式,这样你就可以使用标准的 Promise 组合技术,例如 Promise.all。如果你正在使用其中之一,你只需要像这样做:something

function copyUpdatedFiles(lines) {
    return Promise.all(
        // CONCEPTUAL, semantics will depend on the promise wrapper lib
        lines.map(line => thePromiseWrapper(fs.copy, line, target))
    );
}

附注:你的循环条件引用了一个在代码中没有定义的变量linesLength。应该是lines.length

更具体地说,您不能多次拒绝和解决一个承诺,所以如果您解决了它,请解决一次,如果您在for循环中拒绝,用reject + break或其他方式拒绝。 - atrifan
@atrifan:问题不在于重复调用,而是过早地进行第一次调用。(重复调用 reject/resolve 是无意义的,但并非此处问题的根本原因。) - T.J. Crowder

0

在解决承诺之前,您不必等待复制成功,for之后,所有的fs.copy都已经放入调用堆栈中,但它们没有完成。

您可以在fs.copy的回调函数中使用计数器,并在每个回调被调用时调用resolve,或者使用async

var async = require('async');
var lines  = stdout.split(/\r?\n/);

copyUpdatedFiles(lines).then(
    function() {
       console.log('this one should be executed after copy operation');
    }
);

function copyUpdatedFiles(lines) {
    return new Promise(function(resolve, reject) {
        async.map(lines, (line, callback) => {
            fs.copy(line, target, (err) => {
                callback(err);
            });
        },
        (err) => {
            if(err) {
                reject();
            } else {
                resolve();
            }
        });
    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接