能否在Promise.all中用Array.forEach?代码inputIds异常排查
TypeScript异步函数问题排查
核心代码与疑问
我们的filterStatus函数用于基于allStatus条件过滤inputIds中的元素:
export async function filterStatus(extractionId: string, inputIds: Set<string>, allStatus: StatusType[]): Promise<string[]> { const result: string[] = []; await Promise.all(allStatus.map(async (status) => { const subextStatus = await queryDynamoDB(status, ["id"]); // queryDynamoDB() returns Promise<AWS.DynamoDB.AttributeMap[]> 内部已用await处理异步调用 subextStatus.forEach(v => { const dynamoData = AWS.DynamoDB.Converter.unmarshall(v); if (!dynamoData || !("id" in dynamoData)) { throw new Error(`Unable to unmarshall dynamo items to "id": ${util.inspect(dynamoData)}`); } if (inputIds.has(v["id"])) { result.push(v["id"]); } }); })); return result; }
疑问1:forEach的使用判断是否正确
之前看到资料说forEach中最好不要运行async/await函数,但此处subextStatus已通过await拿到resolved状态的AWS.DynamoDB.AttributeMap[],且Array.forEach是阻塞的,这个判断是否正确?
疑问2:inputIds偶尔变为undefined的原因
调用方已确认inputIds有值,但有时会变为undefined,导致inputIds.has()调用失败。是否存在filterStatus函数返回后forEach仍在执行的可能?
调用方相关代码
export async function getRecentSubext(): Promise<Map<string, Set<string>>> { const results = new Map<string, Set<string>>(); for (const extractor in ExtractorType) { const oneQueryResult = await someDynamoDBCall(extractor); const queryResults: AWS.DynamoDB.AttributeMap[] = oneQueryResult.Items; // 注意:此处v未定义,存在明显错误 const dynamoData = AWS.DynamoDB.Converter.unmarshall(v); if (!dynamoData || !(["id", "extraction_id"].every(element => element in dynamoData))) { throw new Error(`Unable to unmarshall dynamo items to "id", "extraction_id": ${util.inspect(dynamoData)}`); } if (!results.has(dynamoData["extraction_id"])) { results.set(dynamoData["extraction_id"], new Set<string>()); } results.get(dynamoData["extraction_id"])!.add(dynamoData["id"]); } return results; } export async function handler(): Promise<void> { // 省略部分代码 const candidateSubexts: Map<string, Set<string>> = await getRecentSubext(); // 内部已用await确保阻塞执行 for (const extraction of extractions) { const subExtractionCount: CountData = await countSubexts(); // 该函数内部存在问题:有时直接返回async函数而非return await asyncFunc if (candidateSubexts.has(extraction.id)) { await filterStatus(extraction.id, candidateSubexts.get(extraction.id), allStatus); } } // 其他逻辑 }
问题分析与结论
关于forEach的使用判断
你的判断完全正确。此处forEach是同步阻塞执行:
subextStatus已经通过await queryDynamoDB获取到了resolved的数组,不存在异步等待的情况;forEach遍历数组时,内部没有任何异步逻辑,所有循环操作都会在当前事件循环内执行完毕,不会出现异步延迟。
关于inputIds变为undefined的可能性
不可能出现filterStatus返回后forEach仍在执行的情况:
await Promise.all(...)会等待所有由allStatus.map生成的异步函数全部执行完成(包括内部的forEach循环),才会继续执行后续的return result逻辑;- 函数返回时,所有异步操作都已完成,不会有残留的
forEach循环在后台运行。
inputIds偶尔undefined的根因排查
从调用方代码看,最可能的问题出在getRecentSubext函数中:
- 代码中直接使用
const dynamoData = AWS.DynamoDB.Converter.unmarshall(v);,但v并未定义(应该是遍历queryResults数组的每个元素,比如添加queryResults.forEach(v => { ... })包裹相关逻辑); - 这个错误会导致
dynamoData异常,进而使resultsMap的结构不符合预期,后续candidateSubexts.get(extraction.id)可能返回undefined,即使前面判断了candidateSubexts.has(extraction.id)。
另外,虽然countSubexts的返回值未被filterStatus使用,但如果其内部未正确处理异步操作,可能导致事件循环切换,但结合代码逻辑,这不是inputIds变为undefined的直接原因,优先级低于getRecentSubext中的明显错误。
内容的提问来源于stack exchange,提问作者user2988877
相关产品推荐
相关产品推荐

