Функция nodejs then() выполняется до разрешения промиса

У меня проблема с тем, чтобы 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();
    });
}

Пожалуйста, помогите, потому что я явно что-то упускаю.


person Łukasz Korona    schedule 15.07.2016    source источник


Ответы (2)


Она разрешается, как только вы вызываете 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.all. Если бы вы использовали один из них, вы бы просто сделали что-то это:

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.

person T.J. Crowder    schedule 15.07.2016
comment
Чтобы быть более конкретным, вы не можете отклонить и разрешить обещание несколько раз, поэтому, если вы разрешаете его, разрешайте его один раз, а если вы отклоняете for, reject + break или каким-либо другим способом сделать это. - person atrifan; 15.07.2016
comment
@atrifan: проблема не в повторных звонках, а в слишком раннем первом звонке. (Повторный вызов reject/resolve бессмысленен, но не является здесь основной причиной проблемы.) - person T.J. Crowder; 15.07.2016

Вы не ждете успешного копирования перед разрешением промиса, после for все fs.copy были помещены в стек вызовов, но они не завершились.

Вы можете либо использовать счетчик внутри обратного вызова fs.copy и вызывать разрешение после вызова каждого обратного вызова, либо использовать 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();
            }
        });
    });
}
person DrakaSAN    schedule 15.07.2016