Почему в цикле есть проблема со сроками выполнения моих обещаний и как я могу ее решить? - PullRequest
0 голосов
/ 11 сентября 2018

Я схожу с ума по этой проблеме в моем проекте узла хобби. У меня есть функция (processDataSet), которая обрабатывает массив данных (inputArray) и возвращает обещание. Функция использует цикл for для итерации по входному массиву и вызывает функцию saveObjectData в каждом раунде. Эта функция сохранения обрабатывает одну запись данных, а также возвращает обещание.

Похоже, что в случае сбоя функции saveObjectData функция processDataSet отлавливает возвращенное отклонение, но ее собственный reject не вызывается должным образом внутри цикла for. Я считаю, что это проблема времени, которую я не понимаю. Смотрите результаты вывода отпечатков под кодом.

function processDataSet(inputArray, scriptConfig) {
    var contentType = scriptConfig.contentType;
    return new Promise(function(resolve, reject) {
        if(!Array.isArray(inputArray)) {
            return reject(new Error("Input data is not an array. Cannot process."));
        }
        if(!scriptConfig) {
            return reject(new Error("Invalid scriptConfig"));
        }
        if(!typeof contentType === "string" && !contentType instanceof String) {
            return reject(new Error("Invalid contentType for the data set. The parameter should be a string."));
        }

        console.log("Post processing data for the script " + scriptConfig.name + " (type: " + scriptConfig.contentType + ")");

        // Iterate through the input array and handle the objects one-by-one
        for (var i = 0; i < inputArray.length; i++) {
            saveObjectData(inputArray[i], scriptConfig)
            .then(() => {
                //continue;
            })
            .catch(err => {
                console.log("TEST PRINT " + scriptConfig.name);
                return reject(new Error("Processing data object failed.", err));
            });
        }
        console.log("Resolve " + scriptConfig.name);
        return resolve();
    });
}

Вывод на печать в консоли:

Post processing data for the script Script1 (type: Season)
Resolve Script1
TEST PRINT Script1

Кажется, что последняя строка журнала, включая «Resolve ...», печатается перед «TEST PRINT ...» в обработчике ошибок. Почему так и как я могу выполнить выполнение, чтобы дождаться полного разрешения всех записей данных, прежде чем вернуться из processDataSet?

Я не совсем уверен, если в моем случае излишне делать processDataSet для возврата обещаний, но я сделал это как часть моего устранения неполадок.

Ответы [ 3 ]

0 голосов
/ 11 сентября 2018

Ваш цикл асинхронный и возвращается немедленно.

    for (var i = 0; i < inputArray.length; i++) {
        saveObjectData(inputArray[i], scriptConfig)
        .then(() => {
            //continue;
        })
        .catch(err => {
            console.log("TEST PRINT " + scriptConfig.name);
            return reject(new Error("Processing data object failed.", err));
        });
    }

Вам нужно обещание от каждой итерации, дождитесь всех обещаний, а затем позвоните

console.log("Resolve " + scriptConfig.name);
        return resolve();

Примерно так:

const promises = []
// Iterate through the input array and handle the objects one-by-one
for (var i = 0; i < inputArray.length; i++) {
  promises.push(saveObjectData(inputArray[i], scriptConfig))
}

Promise.all(promises).then(results => {
  resolve();
})

Вы часто будете видеть функцию карты, используемую в этом случае ...

const promises = inputArray.map(it => saveObjectData(it, scriptConfig))
0 голосов
/ 11 сентября 2018

saveObjectData является асинхронным, и вы не ожидаете его завершения. Вы должны дождаться его завершения в .then или .catch. То есть ваш console.log('Resolve ' ... должен быть выполнен в .then (или .catch). Конечно, поскольку у вас есть цикл обещаний, вы хотите подождать, пока все они завершатся. Вы можете подождать, пока массив обещаний завершится с Promise.all.

function processDataSet(inputArray, scriptConfig) {
    const contentType = scriptConfig.contentType;
    return new Promise(function(resolve, reject) {
        if(!Array.isArray(inputArray)) {
            return reject(new Error("Input data is not an array. Cannot process."));
        }
        if(!scriptConfig) {
            return reject(new Error("Invalid scriptConfig"));
        }
        if(!typeof contentType === "string" && !contentType instanceof String) {
            return reject(new Error("Invalid contentType for the data set. The parameter should be a string."));
        }
        resolve();
    }).then(() => {
        console.log("Post processing data for the script " + scriptConfig.name + " (type: " + scriptConfig.contentType + ")");

        return Promise.all(inputArray.map(input =>
            saveObjectData(input, scriptConfig)
            .then(() => {
                //continue;
            })
            .catch(err => {
                console.log("TEST PRINT " + scriptConfig.name);
                return reject(new Error("Processing data object failed.", err));
            })
        ));
    }).then(() => console.log(`Resolve ${scripConfig.name}`));
}

Я думаю, что использование async / await облегчит эту задачу.

async function processDataSet(inputArray, scriptConfig) {
    const contentType = scriptConfig.contentType;
    if (!Array.isArray(inputArray)) {
        throw new Error("Input data is not an array. Cannot process.");
    }
    if (!scriptConfig) {
        throw new Error("Invalid scriptConfig");
    }
    if (!typeof contentType === "string" && !contentType instanceof String) {
        throw new Error("Invalid contentType for the data set. The parameter should be a string.");
    }

    console.log("Post processing data for the script " + scriptConfig.name + " (type: " + scriptConfig.contentType + ")");

    await Promise.all(inputArray.map(async input => {
        try {
            saveObjectData(input, scriptConfig);
            // continue
        } catch (err) {
            console.log("TEST PRINT " + scriptConfig.name);
            throw new Error("Processing data object failed.", err);
        }
    }));
    console.log(`Resolve ${scriptConfig.name}`);
}
0 голосов
/ 11 сентября 2018

Ваш цикл for не сохраняет объекты один за другим. Он начинает сохранять первое, затем второе и т. Д., Затем цикл заканчивается, и вы немедленно разрешаете свое обещание. Только после этого обещания, созданные в цикле, будут выполнены, и некоторые из них могут попытаться отклонить уже выполненное обещание.

Избегайте конструктора antipattern и вместо этого правильно соединяйте свои обещания.

Если вы в порядке немедленного запуска всех сохранений, чтобы они выполнялись одновременно, вы можете дождаться всех обещаний с помощью Promise.all:

function processDataSet(inputArray, scriptConfig) {
    if (!Array.isArray(inputArray)) {
        return Promise.reject(new Error("Input data is not an array. Cannot process."));
    }
    if (!scriptConfig) {
        return Promise.reject(new Error("Invalid scriptConfig"));
    }
    var contentType = scriptConfig.contentType;
    if (typeof contentType !== "string") {
        return Promise.reject(new Error("Invalid contentType for the data set. The parameter should be a string."));
    }

    console.log("Post processing data for the script " + scriptConfig.name + " (type: " + scriptConfig.contentType + ")");

    return Promise.all(inputArray.map(input => {
        return saveObjectData(input, scriptConfig)
        .catch(err => {
            console.log("TEST PRINT " + scriptConfig.name);
            throw new Error("Processing data object failed.", input, err);
        });
    })).then(results => {
        console.log("Resolve " + scriptConfig.name, results);
        return;
    });
}

Если вы настаиваете на их последовательном сохранении, я рекомендую использовать async / await.

async function processDataSet(inputArray, scriptConfig) {
    if (!Array.isArray(inputArray)) {
        throw new Error("Input data is not an array. Cannot process.");
    }
    if (!scriptConfig) {
        throw new Error("Invalid scriptConfig");
    }
    var contentType = scriptConfig.contentType;
    if (typeof contentType !== "string") {
        throw new Error("Invalid contentType for the data set. The parameter should be a string.");
    }

    console.log("Post processing data for the script " + scriptConfig.name + " (type: " + scriptConfig.contentType + ")");

    for (var input of inputArray) {
        try {
             await saveObjectData(input, scriptConfig);
        } catch (err) {
            console.log("TEST PRINT " + scriptConfig.name);
            throw new Error("Processing data object failed.", input, err);
        }
    }
    console.log("Resolve " + scriptConfig.name);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...