我可以在不进行批处理的情况下使用数据加载器吗

Can I use data loader without batching

我对数据加载器真正感兴趣的是每个请求的缓存。例如说我的 graphql 查询需要调用 getUser("id1") 3x。我想要一些东西来删除那个电话。

然而,对于数据加载器,我似乎需要将一组键传递到我的批处理函数中,并且多个请求将被批处理到一个 api 调用中。

这让我做出了一些我不喜欢的假设:

1.) 我调用的每个服务都有一个批处理 api(我处理的一些服务没有)。

2.) 如果多个调用被合并为 1 个 api 调用,并且该调用因未找到其中 1 个项目而失败怎么办。通常我可以通过为该字段返回 null 来处理这个问题,这可能是一个有效的案例。然而,现在我的整个调用可能会失败,如果批处理 API 决定抛出一个错误,因为找不到 1 个项目。

有单键请求的数据加载器吗?

这两种假设都是错误的,因为批处理功能的实现最终取决于您。如 documentation 中所示,编写批处理函数时的唯一要求如下:

A batch loading function accepts an Array of keys, and returns a Promise which resolves to an Array of values or Error instances.

所以底层数据源不需要也接受 ID 数组。并且不需要一个或多个失败的调用导致整个函数抛出,因为您可以 return 数组中任何特定 ID 的空值或错误实例 return。事实上,您的批处理函数应该 永远不会 抛出,而应该总是 return 一个或多个错误的数组。

换句话说,批处理函数的实现可能类似于:

async function batchFn (ids) {
  const result = await Promise.all(ids.map(async (id) => {
    try {
      const foo = await getFooById(id)
      return foo
    } catch (e) {
      // either return null or the error
    }
  }))
}

值得注意的是,也可以将 maxBatchSize 设置为 1 以有效禁用批处理。但是,这不会改变批处理函数的实现方式的要求——它总是需要一个 ID 数组,并且总是需要 return 一个 values/errors 的数组,其长度与ID 数组。

Daniel 的解决方案非常好,实际上是我目前使用的解决方案,在将其提取到辅助函数中之后。 但我刚刚找到了另一个不需要那么多样板代码的解决方案。

new DataLoader<string, string>(async ([key]) => [await getEntityById(key)], {batch: false});

当我们设置 batch: false 时,我们应该总是得到一个大小为 1 的键数组作为参数传递。因此,我们可以简单地对其进行解构,并 return 一个包含数据的单一大小的数组。注意 return 值周围的括号!如果你忽略这些,这可能会出错,例如对于字符串值。