【问题标题】:How to avoid await inside a loop如何避免在循环中等待
【发布时间】:2021-06-21 13:17:28
【问题描述】:

我有以下代码:

const data = res?.advancedResult?.data;
for (const referential of data) {
    const entities = await EntityDAO.find({ query: { referential: referential?._id } });
    for (const entity of entities) {
        const items = await ItemDAO.find({ query: { entity: entity?._id } });
        entity.items = items.length;
    }
    referential.entities = entities.map(entity => ({ name: entity.name, items: entity.items }));
}

由于我使用的是 eslint,所以出现以下错误:

[eslint] Unexpected `await` inside a loop. (no-await-in-loop)

我不能使用Promise.all,因为循环的迭代实际上并不是相互独立的。
有没有办法避免循环内部的等待,这样我就可以提高性能?或者我应该通过添加/* eslint-disable no-await-in-loop */来消除错误

【问题讨论】:

  • 试试。然后,不确定这是否会有所作为。
  • 在我看来,您不必按顺序运行任一循环的数据库操作,因为循环的第二次迭代不依赖于循环的第一次迭代中的任何内容,因此它们似乎对所有人彼此独立。因此,您可以使用Promise.all() 并行运行它们。
  • 内循环的迭代对我来说似乎是相互独立的。
  • ...外循环的迭代也是如此。

标签: javascript node.js eslint lint


【解决方案1】:

您似乎遇到了经典的“N+1 选择”问题。有几种方法可以解决 - 最后一种更可取:

  • 如果性能不是问题,请保持原样,并尝试挑战您的 eslint 规则。毕竟,确实有人制定了这条规则。问那个人为什么会这样,他们试图用它解决什么问题 - 也许它背后隐藏着智慧。
  • 您可以并行加载项目(内部循环),以便同时加载所有实体的项目。但是,您冒着获得无限并发的风险 - 如果第一个查询获得 100 个实体,那么您将并行启动 100 个查询。谨防这种情况(参见 p-limit 等模块)。
  • 您可以构建更扁平的查询:获取所有实体,收集它们的 ID,然后构建一个合并 ID 的大查询。我不确定您使用的是什么数据库,但您正在寻找相当于 SQL WHERE entity IN (value1, value2, ...) 的数据库。然后,一旦你得到所有的项目,迭代它们并将每个分配给它的父实体。这样一来,您只能获得 2 个查询(以增加客户端复杂性为代价)。

总的来说,最好解决实际问题(N+1 个查询),而不是弯曲异步代码来实现它。如果您正在编写类似于 ORM 的内容,则可能值得探索现有的实现以及它们如何处理这个经过深入研究的问题。

【讨论】:

  • 无法判断,但问题的措辞向我暗示,就像 OP 正在使用现有的 ORM。在这种情况下,我会回应您编写更扁平查询的建议,或者更好地查阅 ORM 文档以查看 JOIN 或类似的是否可以满足此要求
【解决方案2】:

也许你可以使用 map 函数,map 函数是异步的并且可以等待。

const data = res?.advancedResult?.data;
const temp = data.map(async(referential)=>{
    return await EntityDAO.find({ query: { referential: referential?._id } 
});

add here the rest...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多