专业的编程技术博客社区

网站首页 > 博客文章 正文

前端实用小技巧: 自动分批的网络请求

baijin 2024-10-20 04:07:48 博客文章 11 ℃ 0 评论

上一篇 前端实用小技巧: 自动合并的网络请求 我们聊了如何自动把网络请求收集并合并,今天我们就来聊聊自动分批网络请求。

那么问题来了,什么情况下需要分批请求呢。想象一下有这么一个场景,你有一个接口,在执行的时候会做一些比较复杂的操作,比如要请求多个其他的服务才能收集到需要的数据。这时候如果我发送一个请求,直接向后端请求一万条数据会怎么样?

有两种可能的情况: 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

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表