如何改写含多顺序依赖Promise的代码,避免嵌套await调用
解决forEach回调中无法使用await的异步清理问题
我在编写测试用例的清理阶段,需要依次完成三个步骤:
- 删除所有集合
- 确认集合已删除
- 重建集合
已知createCollection和dropCollection方法返回Promise,必须按顺序等待每个操作完成,但原代码在forEach回调里使用await导致错误,请问该如何改写?
问题代码
const collectionsBeforeDrop = await db.collections(); collectionsBeforeDrop.forEach(c => { console.log(`got collection name : ${c.collectionName}`); await db.dropCollection(c); <-- can't use await here }); console.log(`checking after cleanup`) const collectionsAfterDrop = await db.collections(); collectionsAfterDrop.forEach(c => { console.log(`got collection name after claanup! THIS SHOULDN'T HAVE HAPPENED : ${c.collectionName}`); throw new Error("couldn't clean up after test case"); }); console.log(`creating collections again`); collectionsBeforeDrop.forEach(c => { console.log(`got collection name : ${c.collectionName}`); await db.createCollection(c) <-- can't use await unless at top level .then(()=>{ console.log(`checking after recreating collections`) await db.collections() <-- can't use await unless at top level .then((collectionsAfterRecreation)=>{ collectionsAfterRecreation.forEach(c => { console.log(`got collection name after claanup! THIS SHOULDN'T HAVE HAPPENED : ${c.collectionName}`); }); }) }) });
核心问题原因
forEach的回调是普通函数(非async),没法直接用await;而且forEach本身不会等待异步操作完成,会直接跳去执行后续代码,导致操作顺序完全混乱。
改写方案
用for...of循环(要求顺序执行时首选)或者Promise.all(允许并行执行时用)替代forEach,既能正确使用await,又能保证操作按预期完成。
完整改写代码(顺序执行版)
// 1. 获取所有待处理的集合 const collectionsBeforeDrop = await db.collections(); // 按顺序删除每个集合 for (const c of collectionsBeforeDrop) { console.log(`正在删除集合: ${c.collectionName}`); await db.dropCollection(c.collectionName); // 注意:部分驱动需要传集合名字符串,而非对象,按需调整 } // 2. 确认集合已全部删除 console.log(`检查清理结果`); const collectionsAfterDrop = await db.collections(); if (collectionsAfterDrop.length > 0) { const remainingCollections = collectionsAfterDrop.map(c => c.collectionName).join(', '); throw new Error(`清理失败,剩余未删除集合: ${remainingCollections}`); } // 3. 按顺序重建集合 console.log(`开始重建集合`); for (const c of collectionsBeforeDrop) { console.log(`正在创建集合: ${c.collectionName}`); await db.createCollection(c.collectionName); } // 可选:验证重建结果 console.log(`检查重建后的集合`); const collectionsAfterRecreation = await db.collections(); const recreatedNames = collectionsAfterRecreation.map(c => c.collectionName); collectionsBeforeDrop.forEach(c => { if (!recreatedNames.includes(c.collectionName)) { throw new Error(`集合 ${c.collectionName} 未成功重建`); } }); console.log(`清理与重建操作完成`);
关键改动说明
- 用
for...of替代forEach:for...of支持在循环体内用await,会等当前异步操作完成后再进入下一次循环,严格保证顺序。 - 移除嵌套
then():改用await链式调用,代码结构更清晰,避免回调地狱。 - 优化验证逻辑:直接判断集合数组长度,或用
map+includes验证重建结果,比原代码里forEach抛错更直观。 - 适配驱动API:部分数据库驱动的
dropCollection/createCollection需要传入集合名称字符串而非集合对象,根据实际使用的驱动调整参数。
并行执行版(适合无顺序要求的场景)
如果删除和重建操作不需要严格按顺序执行,用Promise.all能提升执行效率:
// 并行删除所有集合 await Promise.all(collectionsBeforeDrop.map(c => db.dropCollection(c.collectionName))); // 并行重建所有集合 await Promise.all(collectionsBeforeDrop.map(c => db.createCollection(c.collectionName)));
这种方式会同时发起所有异步操作,适合对顺序无要求的场景,执行速度更快。
内容的提问来源于stack exchange,提问作者Manu Chadha
相关产品推荐
相关产品推荐

