首页 > 解决方案 > 为什么我的承诺在循环中存在时间问题,我该如何解决?

问题描述

在我的爱好节点项目中,我对这个问题非常恼火。我有一个函数 ( 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

似乎在错误处理程序中的“TEST PRINT ...”之前打印了最后一个日志行,包括“Resolve ...”。为什么会这样,我怎样才能让执行在返回之前等待所有数据条目的完全解析processDataSet

我不完全确定在我的情况下processDataSet返回承诺是否是多余的,但我把它作为我的故障排除的一部分。

标签: javascriptnode.jspromise

解决方案


您的for循环不会一一保存对象。它开始保存第一个,然后是第二个,依此类推,然后循环结束,您立即解决您的承诺。只有在那之后,在循环中创建的 Promise 才会解决,其中一些可能会尝试拒绝已经实现的 Promise。

避免使用Promise构造函数 antipattern,而是正确地链接你的 Promise。

如果您可以立即触发所有保存以便它们同时运行,您可以等待所有的承诺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);
}

推荐阅读