嵌套异步等待未按预期工作
Nested async await not working as expected
我正在尝试在现有代码中为某些数据添加 table,为此我设置了与 exceljs 文档中提到的内容类似的所有内容。这是我试图获取我需要的数据以获取 table 的条件的代码片段。当我在 forEach 中打印 modRows 时,它会显示数据,但是当我在循环外打印时,它会变成空白。有什么办法或其他解决方法吗?异步中是否可以有异步?
const generateCatReports = async (mode = 'monthly', months = 1, toDate = false) => {
if (!['monthly', 'weekly'].includes(mode)) {
throw new Error(InvalidParamsError);
}
const sortedTRIds = obtainSortedCentreIds(studiesTRMap, centreNameIDMap);
const modRows = [];
sortedTRIds.forEach(async (trId) => {
console.log("trid",trId);
const statsParams = {
catId: trId,
createdAt,
};
const statsPipeline = studiesStatsPipeline(statsParams, capitalize(mode));
const statsInfo = await StudyModel.aggregate(statsPipeline).allowDiskUse(true).exec();
Object.entries(statsInfo[0]).slice(1).forEach(([key, val]) => {
modRows.push([key, val]);
});
console.log("inside sortedTr loop>>>>>" ,modRows);
});
console.log("outside sortedTr loop>>>>>>>>",modRows);
}
结果:
trId 1cf1eb1324322bbebe
inside sortedTr loop>>>>>>>>>>
['TOTAL', 10]
['white', 5]
['black', 5]
trId 1cf1eb1324322bbebe
inside sortedTr loop>>>>>>>>>>
['TOTAL', 10]
['white', 5]
['black', 5]
trId 21e1eb21322bbebe
inside sortedTr loop>>>>>>>>>>
['TOTAL', 8]
['white', 6]
['black', 2]
outside sortedTr loop>>>>>>>>>>
[]
async/await
不能在forEach
循环中使用。
你可以在一个简单的for循环或一段时间内使用它,做...
例如:
export async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
每当您看到像 .forEach(async (trId) => {
这样的异步回调时,出错的可能性就很高。
您遇到的问题是 async
函数实际上是承诺,因此它们不会阻塞主线程。这意味着您的回调函数在作业队列中排队,并将在将来执行。
它的简化是这样的:
let arr = []
setTimeout(() => {
arr.push("hy")
})
console.log(arr)
arr 将是空的,
但是您可以使用 for ... of
循环
const generateCatReports = async (
mode = "monthly",
months = 1,
toDate = false
) => {
if (!["monthly", "weekly"].includes(mode)) {
throw new Error(InvalidParamsError);
}
const sortedTRIds = obtainSortedCentreIds(studiesTRMap, centreNameIDMap);
const modRows = [];
for (const trId of sortedTRIds) {
const statsParams = {
catId: trId,
createdAt
};
const statsPipeline = studiesStatsPipeline(statsParams, capitalize(mode));
const statsInfo = await StudyModel.aggregate(statsPipeline)
.allowDiskUse(true)
.exec();
Object.entries(statsInfo[0])
.slice(1)
.forEach(([key, val]) => {
modRows.push([key, val]);
});
console.log("inside sortedTr loop>>>>>", modRows);
}
console.log("outside sortedTr loop>>>>>>>>", modRows);
};
这里没有要排队的回调。
更好的解决方案是使用 Promise.all()
const generateCatReports = async (
mode = "monthly",
months = 1,
toDate = false
) => {
if (!["monthly", "weekly"].includes(mode)) {
throw new Error(InvalidParamsError);
}
const sortedTRIds = obtainSortedCentreIds(studiesTRMap, centreNameIDMap);
const modRows = await Promise.all(
sortedTRIds.flatMap(async (trId) => {
const statsParams = {
catId: trId,
createdAt
};
const statsPipeline = studiesStatsPipeline(statsParams, capitalize(mode));
const statsInfo = await StudyModel.aggregate(statsPipeline)
.allowDiskUse(true)
.exec();
return Object.entries(statsInfo[0]).slice(1);
})
);
console.log(modRows);
};
这就是为什么你不应该在 forEach 循环中使用 async/await,
forEach 循环接受回调,即使您将异步函数作为回调传递也不会等待。您的异步回调将等待其中的任何承诺,但 forEach 循环将继续为数组中的所有元素执行回调函数。
而不是使用 for...in
循环,正如预期的那样,for...in
循环内的 await 将停止循环执行,并且仅在您完成对承诺的等待时继续迭代。
我正在尝试在现有代码中为某些数据添加 table,为此我设置了与 exceljs 文档中提到的内容类似的所有内容。这是我试图获取我需要的数据以获取 table 的条件的代码片段。当我在 forEach 中打印 modRows 时,它会显示数据,但是当我在循环外打印时,它会变成空白。有什么办法或其他解决方法吗?异步中是否可以有异步?
const generateCatReports = async (mode = 'monthly', months = 1, toDate = false) => {
if (!['monthly', 'weekly'].includes(mode)) {
throw new Error(InvalidParamsError);
}
const sortedTRIds = obtainSortedCentreIds(studiesTRMap, centreNameIDMap);
const modRows = [];
sortedTRIds.forEach(async (trId) => {
console.log("trid",trId);
const statsParams = {
catId: trId,
createdAt,
};
const statsPipeline = studiesStatsPipeline(statsParams, capitalize(mode));
const statsInfo = await StudyModel.aggregate(statsPipeline).allowDiskUse(true).exec();
Object.entries(statsInfo[0]).slice(1).forEach(([key, val]) => {
modRows.push([key, val]);
});
console.log("inside sortedTr loop>>>>>" ,modRows);
});
console.log("outside sortedTr loop>>>>>>>>",modRows);
}
结果:
trId 1cf1eb1324322bbebe
inside sortedTr loop>>>>>>>>>>
['TOTAL', 10]
['white', 5]
['black', 5]
trId 1cf1eb1324322bbebe
inside sortedTr loop>>>>>>>>>>
['TOTAL', 10]
['white', 5]
['black', 5]
trId 21e1eb21322bbebe
inside sortedTr loop>>>>>>>>>>
['TOTAL', 8]
['white', 6]
['black', 2]
outside sortedTr loop>>>>>>>>>>
[]
async/await
不能在forEach
循环中使用。
你可以在一个简单的for循环或一段时间内使用它,做...
例如:
export async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
每当您看到像 .forEach(async (trId) => {
这样的异步回调时,出错的可能性就很高。
您遇到的问题是 async
函数实际上是承诺,因此它们不会阻塞主线程。这意味着您的回调函数在作业队列中排队,并将在将来执行。
它的简化是这样的:
let arr = []
setTimeout(() => {
arr.push("hy")
})
console.log(arr)
arr 将是空的,
但是您可以使用 for ... of
循环
const generateCatReports = async (
mode = "monthly",
months = 1,
toDate = false
) => {
if (!["monthly", "weekly"].includes(mode)) {
throw new Error(InvalidParamsError);
}
const sortedTRIds = obtainSortedCentreIds(studiesTRMap, centreNameIDMap);
const modRows = [];
for (const trId of sortedTRIds) {
const statsParams = {
catId: trId,
createdAt
};
const statsPipeline = studiesStatsPipeline(statsParams, capitalize(mode));
const statsInfo = await StudyModel.aggregate(statsPipeline)
.allowDiskUse(true)
.exec();
Object.entries(statsInfo[0])
.slice(1)
.forEach(([key, val]) => {
modRows.push([key, val]);
});
console.log("inside sortedTr loop>>>>>", modRows);
}
console.log("outside sortedTr loop>>>>>>>>", modRows);
};
这里没有要排队的回调。
更好的解决方案是使用 Promise.all()
const generateCatReports = async (
mode = "monthly",
months = 1,
toDate = false
) => {
if (!["monthly", "weekly"].includes(mode)) {
throw new Error(InvalidParamsError);
}
const sortedTRIds = obtainSortedCentreIds(studiesTRMap, centreNameIDMap);
const modRows = await Promise.all(
sortedTRIds.flatMap(async (trId) => {
const statsParams = {
catId: trId,
createdAt
};
const statsPipeline = studiesStatsPipeline(statsParams, capitalize(mode));
const statsInfo = await StudyModel.aggregate(statsPipeline)
.allowDiskUse(true)
.exec();
return Object.entries(statsInfo[0]).slice(1);
})
);
console.log(modRows);
};
这就是为什么你不应该在 forEach 循环中使用 async/await,
forEach 循环接受回调,即使您将异步函数作为回调传递也不会等待。您的异步回调将等待其中的任何承诺,但 forEach 循环将继续为数组中的所有元素执行回调函数。
而不是使用 for...in
循环,正如预期的那样,for...in
循环内的 await 将停止循环执行,并且仅在您完成对承诺的等待时继续迭代。