网站首页 > 博客文章 正文
上一篇 前端实用小技巧: 自动合并的网络请求 我们聊了如何自动把网络请求收集并合并,今天我们就来聊聊自动分批网络请求。
那么问题来了,什么情况下需要分批请求呢。想象一下有这么一个场景,你有一个接口,在执行的时候会做一些比较复杂的操作,比如要请求多个其他的服务才能收集到需要的数据。这时候如果我发送一个请求,直接向后端请求一万条数据会怎么样?
有两种可能的情况: 1. 后端很强,能够处理这么多数据,但是时间不够,会超时,导致后端服务还没来得及把结果返回就被网关层强制中断请求返回503。2. 后端很弱,直接把服务打崩了,返回500.
这两种情况都不是我们想要的,我们期望不管怎么样都应当返回给用户一个结果。如果是一个比较大数据量的操作等待一段时间用户也是有心理预期的。就好像同样是删文件,删一个文件瞬间删除,删十万个文件等上几分钟也是正常的。
这就是为什么我们要做自动分批请求的原因,确保我们的操作不会把后端打崩的前提下让用户能够得到返回结果。
为了方便使用,我的预期调用方式是这样的:
const _getUserInfoList = (ids: string[]): Promise<UserInfo[]> => { /* ... */}
export const getUserInfoList = createAutoSplitRequest(_getUserInfoList)
首先假设我已经有了一个输入ids返回对应数据的接口,取名为 _getUserInfoList。
然后我期望我写的函数能在外面直接包一层,在不改变参数类型的前提下直接让我的请求函数增加上自动分批请求的功能。
废话不多说,直接上代码:
/**
* 创建一个自动拆分请求参数的函数
*/
export function createAutoSplitRequest<Key, Item>(
fn: (keys: Key[]) => Promise<Item[]>,
type: 'serial' | 'parallel',
limit = 100
): (arr: Key[]) => Promise<Item[]> {
return async (arr: Key[]): Promise<Item[]> => {
const groups = _chunk(arr, limit);
if (type === 'serial') {
const list: Item[] = [];
for (const group of groups) {
const res = await fn(group);
if (Array.isArray(res)) {
list.push(...res);
} else {
console.warn('[createAutoSplitRequest] fn should be return array');
}
}
return list;
} else if (type === 'parallel') {
const res = await Promise.all(groups.map((group) => fn(group)));
return _flatten(res);
}
return [];
};
}
除了我们必要的请求函数以外,我还给他加上了两个参数 type 和 limit 。 type 控制分批请求时是使用的并行请求还是串行请求, limit 来控制每批到底有多少个。
那就有同学会问了,不是说分批请求的目的就是避免一次性请求大量数据导致服务器负载过高么?并发这么多请求不是一样会给服务器这么大压力么?有什么区别呢?
区别就是如果后端使用多个服务同时提供服务,请求会被打散到不同的服务上去处理,而更重要的是因为每个请求准备的数据少了,所以能够有办法在不超时的前提下把数据返回给前端。
至于代码则非常简单,首先老样子直接返回一个函数,函数的定义和 fn 的定义一样,然后使用 lodash 的 chunk 方法把传入id列表分组。
如果是串行请求,则构建一个 for 循环,循环调用 fn(group) , group为我们分批后的id列表。把结果推到结果列表 list 中,当所有的操作都完成后,把结果返回.
如果是并行请求,则直接创建一个 Promise.all ,然后构建分组后的数据的请求函数,最后得到的 res 应该是 [ [...], [...] ] 这样的。我们把结果使用 lodash 的 flatten 压平数组即可得到最后请求结果列表。
另外,如果我们的请求函数返回结果是有序的且与入参一一对应的,那么我们分批打散并组装后的结果列表也是有序的且与入参一一对应的
用法则是:
export const getUserInfoList = createAutoSplitRequest(_getUserInfoList, 'serial', 1000);
也可以与上期讲的自动合并网络请求一起使用哦
export const getUserInfo = createAutoMergedRequest(getUserInfoList);
本文实战用法见: tailchat/client/shared/utils/request.ts at master · msgbyte/tailchat · GitHub
我会不定期的从 Tailchat 的源码中拆出一些实用的实战小技巧分享给大家,关注我,并给 Tailchat 点点 star。我将带你以实战理解算法的妙用。
Tailchat - The next-generation noIM Application in your own workspace | Tailchat
猜你喜欢
- 2024-10-20 轻量级的React数据流及状态管理解决方案,支持SSR服务器端渲染
- 2024-10-20 5 分钟掌握 JavaScript 实用窍门(javascript官网)
- 2024-10-20 前端必备的20种基本React工具「干货」
- 2024-10-20 async await 并发(async await会阻塞吗)
- 2024-10-20 8 张图帮你一步步看清 async,await 和 promise 的执行顺序
- 2024-10-20 Atom源码阅读系列一(nasm源码阅读笔记)
- 2024-10-20 2019年JavaScript几个秘密窍门,你知道吗?
- 2024-10-20 小猿圈分享6个提JavaScript 小技巧(下)
- 2024-10-20 js遍历那些事儿(js遍历tr)
- 2024-10-20 Google 开源 zx,用 async/await 编写 shell 脚本
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)