了解循环选择如何影响异步性能
在我最近的项目中,我需要更新 NoSQL 数据库中的大量记录——超过一千条。由于一次性更新它们是不可行的,而且该过程也是异步的,因此我决定以较小的批次(每批 20 条记录)处理它们。我使用 forEach 在循环中实现了这一点,其中每组 20 个将在进入下一组之前更新。然而,在将这种方法付诸实践后,我注意到并非所有记录都按预期更新,即使在这些较小的批次中也是如此。例如,当我尝试更新一批 25 条记录时,只有 10 条记录成功更新。其他10条记录没有日志,更新失败的记录数量随机变化——有时12条,有时5条,有时7条。
经过进一步调试,我发现这些记录很可能在更新过程中被跳过。现在,为什么会发生这种情况?让我们探讨这个问题,以了解该行为并确定其背后的潜在原因。
问题
经过一些研究,我发现在 forEach 循环中使用异步函数而没有适当的机制来等待所有迭代可能会导致问题。在这种情况下,我们在 forEach 循环中使用了 async/await,但没有确保等待所有迭代(请参阅下面的代码):
async function patchRecords(records) { // Here, the length of records is 25 let successfulUpdates = 0; // Initialize a counter for successful updates records.forEach(async (item) => { await databaseName.patch(item); successfulUpdates++; // Increment counter on successful update }); // Return the number of records updated successfully return successfulUpdates; } const response = await patchRecords(records); // Make sure to await this call
在上面的代码中,没有任何机制可以确保循环的所有异步迭代都完成。 forEach 方法会触发多个异步调用来修补记录,但 patchRecords 函数很快到达返回语句,而无需等待这些修补完成。因此,当更新在后台处理时,该功能会继续执行,而无需等待更新完成。这可能会导致一些待处理的承诺仍未兑现,从而可能导致跳过或丢弃更新。
解决方案
那么我们该如何解决这个问题呢?在我们开始讨论解决方案之前,我们首先了解迭代数据的不同方法。从根本上来说,有两种方法:顺序迭代数据或并行。
如果您想以顺序方式异步迭代数据,使用 forEach 循环可能会导致我们讨论的问题。相反,最好使用现代的 for…of 循环或简单的 for 循环,因为它们允许 await 正常工作并确保所有更新都得到处理不会被跳过(请参阅下面的代码)。
async function patchRecords(records) { // Here, the length of records is 25 let successfulUpdates = 0; // Initialize a counter for successful updates records.forEach(async (item) => { await databaseName.patch(item); successfulUpdates++; // Increment counter on successful update }); // Return the number of records updated successfully return successfulUpdates; } const response = await patchRecords(records); // Make sure to await this call
另一方面,如果你想并行处理记录,再次使用 forEach 将不起作用。虽然每个异步回调确实返回一个承诺,但其中一些承诺可能仍然未实现,因为它们没有被等待。相反,使用 map 生成 Promise 数组,然后使用 Promise.all 等待它们(请参阅下面的代码)。
async function patchRecords(records) { let successfulUpdates = 0; // Counter for successful updates for (const item of records) { try { // Query to patch each record await databaseName.patch(item); successfulUpdates++; // Increment counter on success } catch { // Handle any errors } } return successfulUpdates; // Return the number of records updated successfully } const response = await patchRecords(records); // Make sure to await this call
这展示了选择正确的循环方法如何极大地影响异步操作的预期结果。您使用的循环类型 - 无论是 forEach、for...of 还是标准 for 循环 - 对于代码管理异步任务的效果起着至关重要的作用。
总结
我解释清楚了吗?请在下面的评论中告诉我。
我很高兴分享我的经验,希望你也觉得它很有价值!由于我仍在探索这个领域,我希望得到您的反馈和建议。 ☺️ 如果您发现我可以改进的地方,或者有您希望我涵盖的主题,请随时与我们联系! ?网站。我很高兴能在您的帮助下学习和成长。 ??
以上是forEach 与 for:异步对决!的详细内容。更多信息请关注PHP中文网其他相关文章!