我们正在尝试将请求分成多个块,因为外部 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 )}
)
)
我会使用两个函数来实现这一点。
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([]));
根据@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);