在我的爱好节点项目中,我对此问题一无所知。我有一个函数(processDataSet
)正在处理数据数组(inputArray
)并返回一个Promise。该函数使用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
返回承诺是否多余,但是我将其作为故障排除的一部分。
答案 0 :(得分:5)
您的for
循环不会一一保存对象。它开始保存第一个,然后保存第二个,依此类推,然后循环结束,您可以立即解决您的诺言。只有在循环中创建的承诺之后,这些承诺才会成立,并且其中一些可能会拒绝接受已经兑现的承诺。
避免使用Promise
constructor 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);
}
答案 1 :(得分:2)
您的循环是异步的,并立即返回。
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))
答案 2 :(得分:2)
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}`);
}