JavaScript中如何确保异步任务完成后再返回添加元素的列表?
这个问题我太熟了!你遇到的是JavaScript异步编程里的经典坑——数据库查询是异步执行的,你的循环根本不会等每一次查询完成就继续往下走,所以当你返回all_recipe的时候,里面大概率还没被填满数据。而且不用纠结Mutex或者Semaphore哈,JavaScript是单线程的,我们用Promise和async/await就能完美解决这个问题,下面给你两种常用的方案:
首先先把你的异步查询封装成Promise版本(大多数现代数据库库其实已经支持Promise了,如果你的库还在用回调,先做这一步):
// 封装数据库查询为Promise function getRecipeById(id) { return new Promise((resolve, reject) => { db.query('SELECT * FROM recipes WHERE id = ?', [id], (err, result) => { if (err) { reject(err); // 查询失败时抛出错误 } else { resolve(result[0]); // 查询成功时返回单个菜谱 } }); }); }
解决方案1:并行执行所有查询(效率更高)
如果你的查询之间没有依赖关系,推荐用Promise.all一次性发起所有查询,等待全部完成后再收集结果:
async function getAllRecipes() { const someIds = [1, 2, 3, 4]; // 假设这是你要查询的ID列表 // 生成所有查询的Promise数组 const recipePromises = someIds.map(id => getRecipeById(id)); try { // 等待所有Promise完成,得到完整的结果数组 const all_recipe = await Promise.all(recipePromises); // 可选:过滤掉可能的空结果(比如ID不存在的情况) return all_recipe.filter(recipe => recipe); } catch (err) { console.error('查询过程出错:', err); throw err; // 把错误抛出去让调用者处理 } } // 调用方式(注意async函数返回的是Promise,需要用await或者.then处理) getAllRecipes().then(recipes => { console.log('所有查询完成后的菜谱列表:', recipes); // 这里就可以放心使用完整的recipes列表了 }).catch(err => { // 处理错误 });
这种方式的优势是所有查询并行执行,速度更快,适合大多数无依赖的场景。
解决方案2:顺序执行查询(适合有依赖的场景)
如果你的查询需要按顺序执行(比如前一个查询的结果会影响下一个查询的参数),可以用for...of循环结合await:
async function getAllRecipesSequentially() { const someIds = [1, 2, 3, 4]; const all_recipe = []; for (const id of someIds) { try { // 等待当前查询完成后再进行下一个 const recipe = await getRecipeById(id); if (recipe) { all_recipe.push(recipe); } } catch (err) { console.error(`查询ID为${id}的菜谱出错:`, err); // 可以选择继续执行下一个查询,或者抛出错误终止 } } return all_recipe; } // 调用方式 getAllRecipesSequentially().then(recipes => { console.log('顺序查询完成后的菜谱列表:', recipes); });
这种方式会逐个执行查询,速度慢一些,但适合有依赖的场景。
额外说明:为什么不用Mutex?
JavaScript是单线程的,同一时间只会执行一段代码,异步操作是放到事件队列里等待执行的,所以不会出现多线程同时修改all_recipe的竞态条件。你遇到的问题本质是异步操作的执行顺序不确定,导致返回列表时数据还没填充完成,用await或者Promise.all就能确保所有异步操作完成后再返回结果。
内容的提问来源于stack exchange,提问作者Sadij
相关产品推荐
相关产品推荐

