# 前端函数或请求的聚合

> 前端函数或请求的聚合

- 发布于：2021-03-31
- 更新于：2021-03-31
- 分类：软件开发
- 标签：前端
- 原文：https://chanx.tech/blog/frontend-function-aggregation

## 场景
- 某个请求被重复触发
- 某个函数被重复触发

多次触发合并成一次，或一段时间内触发合并成一次。思路其实与**节流**类似

为什么？

- 具体业务场景需要。如`element-ui`中上传组件多选文件会多次触发上传钩子，需要合并成一次上传
- 性能优化考虑。某个高频的http请求进行聚合，降低请求频率



**先举个例子，某个上传函数被多次触发**

```js
const upload = (filename) => {
  console.log("正式上传", filename);
}

upload("a");
upload("b");
upload("c");
```

> 正式上传 a 正式上传 b 正式上传 c

需求： 希望多个文件能放在一次正式上传

## 宏任务

```js
const file = [];
const upload = (() => {
  let collect = null;
  return function(filename){
    file.push(filename); // 记录要上传的文件
    if(!collect){
      collect = setTimeout(()=>{
        console.log("正式上传", file);
        collect = null;
      },0);
    }
  }
})();

upload("a");
upload("b");
upload("c"); 
```

> 正式上传 ["a", "b", "c"]

**思路：三次上传钩子实际上是同步代码，宏任务会在同步代码执行完成后再去触发。利用闭包记录下是否已经设置宏任务和需要上传的文件**

## 微任务

```js
const file = [];
const upload = (() => {
  let collect = null;
  return function(filename){
    file.push(filename);
    if(!collect){
      collect = Promise.resolve().then(()=>{
        console.log("正式上传", file);
        collect = null;
      })
    }
  }
})();



upload("a");
upload("b");
upload("c"); 
```

> 正式上传 ["a", "b", "c"]

**思路：其实跟宏任务一样，只不过`promise.then`会在同步代码后，`setTimeout`前触发**



## 小结

总的来说，就是对原有的上传钩子进行了**封装**，或者叫**代理**。并利用**事件循环**的执行顺序，在合适的时机再去执行上传操作。

> Q：那是不是只有以上两种方案呢？当然不是。思路一致，但是实现各有不同。
>
> A：比如说，知道触发次数，可以考虑使用计数器在最后一次触发；标记变量可以使用其他写法来实现；
>
> Q：我只想聚合一部分，怎么办？
>
> A：`setTimeout`设置时间；或者设置临界值再推几个宏/微任务，注意记录文件做相应修改
>
> Q：那异步触发的怎么去做聚合？
>
> A：我可能是利用一下`Promise.all`吧。没有深入想，此处不多说。
>
> 说到聚合 + 性能优化，其实还有后端接口的聚合。利用中间层进行接口聚合，前端再去请求聚合后的接口，也能优化前端体验
