Javascript |使用 Promise.all 将请求拆分为块

问题描述 投票:0回答:2

我们正在尝试将请求分成多个块,因为外部 API 对我们每页可以显示的产品数量有限制。

假设我们一共有 113 个产品,但每页只显示 5 个,这是通过传入产品 id 作为参数来获取的(

productIds[]=x&productIds[]=y
)。我们知道总共有 113 个,限制为 5 个,但是,我们不想通过等待前一个请求完成来减慢速度,因此我们想使用
Promise.all()
对其进行分块。

我知道我可以使用

slice
来实现此目的,但是,我希望我能够将其映射到数组中。

所以我们有一个开始数组,就像

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, ...]

这些是所有产品 ID,然后我们需要为引用这些 ID 的每 5 个产品发送一个请求。

await axios.get('http://external.api/product', { params: { productIds: productIds.slice(0, 5) } }

但是,我想做如下的事情:

Promise.all(
  productIds.map(
    product => axios.get('...', { params: {productIds: (subset of 5 products in chunks )}
  )
)
javascript promise
2个回答
1
投票

我会使用两个函数来实现这一点。

chunk
promiseAll
delay
chunk

这是我用来按计数对列表进行分组的块函数。

const chunk = <T>(arr: T[], size: number): T[][] => [
  ...Array(Math.ceil(arr.length / size)),
].map((_, i) => arr.slice(size * i, size + size * i));

还有 Promise 的延迟功能。

const delay = (ms: number) => {
  return new Promise<void>((resolve) => setTimeout(resolve, ms));
}

如果API没有任何时间限制,你可以使用这个:

const groupedIdList = chunk(idList, PAGESIZE);
const result = await Promise.all(groupedIdList.map(idList) => myFetchFunctionPromise(idList));
const data = result.flat();

如果 API 有一些限制,您需要延迟您的请求。

  const data = await groupedIdList.reduce(async (prev, subIdList) => {
    const result = await prev;
    const newDataList = await myFetchFunctionPromise(subIdList);
    // wait some time ms
    await delay(500);
    return [...result, ...newDataList];
  }, Promise.resolve([]));

0
投票

根据@hurricane的回答,我创建了一个通用函数来分块调用异步函数:

/**
 * Calls many async functions in chunks and returns the accumulated results of all chunks in one flattened array.
 * @param asyncFunctions A list of functions that make an async call and should be called in chunks. I.e. `() => this.service.loadData()`
 * @param chunkSize how many async functions are called at once
 */
export async function callInChunks<T>(asyncFunctions: (() => Promise<T>)[], chunkSize: number): Promise<T[]> {
  const numOfChunks = Math.ceil(asyncFunctions.length / chunkSize);
  const chunks = [...Array(numOfChunks)].map((_, i) => asyncFunctions.slice(chunkSize * i, chunkSize * i + chunkSize));

  const result: T[] = [];
  for (const chunk of chunks) {
    const chunkResult: T[] = await Promise.all(chunk.map(chunkFn => chunkFn()));
    result.push(...chunkResult);
  }
  return result.flat() as T[];
}

我知道在你的情况下它并不适合,因为你不需要成块地发出单个请求。但如果您一次只能通过 id 加载一个产品,情况会是这样。

const requests = productIds.map(
  productId => () => axios.get<Product>(`https://localhost/api/product/${productId}`)
);

const result: Product[] = await callInChunks(requests, 5);
© www.soinside.com 2019 - 2024. All rights reserved.