大部分人对于 Promise 的开发程度远远不足 10%!

大家都知道 Promise 是 JavaScript 的一个非常强大的特性,但是很多人对于 Promise 的认识还停留在 网络请求上 ,而网络请求大部分人都用 axiosaxios 已经帮我们封装好了,所以自然而然地,大部分人在项目中并没有过多使用过 Promise 这个强大的特性

接下来总结一下我在项目中用到的 Promise 的场景

打开弹窗的事件回调

如下场景,因为逻辑比较多,我封装了一个 useModal 的 Hook,并在页面中去调用,打开弹窗,打开弹窗的时候,接收两个回调:

  • onSuccess:成功的回调
  • onError:失败的回调

因为我需要在页面调用的时候,成功和失败之后都需要在页面中做一些额外的操作

接着我在页面中去调用这个 Hook,并使用了 open 方法,传入两个回调

不过后来我想了一下,如果我对于这个 Modal,只在乎 成功回调失败回调 的话,那可不可以直接 链式调用就行了呢?,就类似于下面这样,有点类似于那些组件库中的 form.validate 方法,也是返回一个 Promise

那么就得借助 Promise 的力量了

充当发布订阅

说到发布订阅,大家就想到了 EventBus、EventEmit 这些具备 emit、on 的元素,但是有时候我们所需要的 发布订阅 并不需要这么复杂

我们需要的可能只是,页面1 完成了某件事情后,通知一下 页面2,仅此而已

我们需要先封装一个 onReadyFn 的工具函数,这函数返回两个东西

  • readyResolve: 一个resolve函数
  • onReady: 接收回调函数,只有在readyResolve执行后才会执行

接着封装一个 usePage1.ts,用来处理 Page1 的逻辑,并在处理完逻辑后发布通知

现在通知有了,只需要订阅即可

可以到控制台里,看到了这个运行顺序是符合我们的预期的

检测接口请求超时

我们使用 axios 的时候可以设置 timeout 去控制接口的超时时间,但是有的项目并没有用 axios 那咋办呢?那就得自己去检测接口请求超时啦

大概思路是这样的,比如你设置的超时时长是 2000ms

  • 让实际请求跟一个 2000ms 的定时器去赛跑
  • 定时器先执行完,则说明实际请求超时了
  • 实际请求先执行完,则说明没有超时

说到赛跑,咱们第一时间可以想到 Promise.race,是的,就是用它来检测接口超时

控制多并发

提起控制并发,大家应该不陌生,我们可以先来看看多并发,再去聊聊为什么要去控制它

多并发一般是指多个异步操作同时进行,而运行的环境中资源是有限的,短时间内过多的并发,会对所运行的环境造成很大的压力,比如前端的浏览器,后端的服务器,常见的多并发操作有:

  • 前端的多个接口同时请求
  • 前端多条数据异步处理
  • Nodejs的多个数据操作同时进行
  • Nodejs对多个文件同时进行修改

正是因为多并发会造成压力,所以我们才需要去控制他,降低这个压力~,比如我可以控制最大并发数是 3,这样的话即使有100个并发,我也能保证最多同时并发的最大数量是 3

大致实现思路就是,假设现在有 9 个并发,我设置最大并发为 3,那么我将会走下面这些步骤:

  • 1、先定好三个坑位
  • 2、让前三个并发进去坑位执行
  • 3、看哪个坑位并发先执行完,就从剩余的并发中拿一个进去补坑
  • 4、一直重复第 3 步,一直到所有并发执行完

在进行多并发的时候,我们通常会使用 Promise.all ,但是 Promise.all并不能控制并发,或者说它本来就没这个能力,我们可以看下面的例子

最后是同时输出,这说明这几个并发是同时发生的

期望效果

实现 limitFn

我们需要在函数内部维护两个变量:

  • queue:队列,用来存每一个改造过的并发
  • activeCount:用来记录正在执行的并发数

并声明函数 generator ,这个函数返回一个 Promise,因为 Promise.all 最好是接收一个 Promise 数组

接下来我们来实现 enqueue 这个函数做两件事:

  • 将每一个 fetchFn 放进队列里
  • 将坑位里的 fetchFn 先执行

假如我设置最大并发数为 2,那么这一段代码在一开始的时候只会执行 2 次,因为一开始只会有 2 次符合 if 判断,大家可以思考一下为什么~

一开始执行 2 次,说明这时候两个坑位已经各自有一个 fetchFn 在执行了

接下来我们实现 run 函数,这个函数是用来包装 fetch 的,他完成几件事情:

  • 1、将 activeCount++ ,这时候执行中的并发数 +1
  • 2、将 fetchFn 执行,并把结果 resolve 出去,说明这个并发执行完了
  • 3、将 activeCount--,这时候执行中的并发数 -1
  • 4、从 queue 中取一个并发,拿来补坑执行

其实第 3、4 步,是在 next 函数里面执行的

代码

充当发布订阅

typescript 复制代码
// hooks/onReadyFn
exportconst onReadyFn = () => {
let readyResolve: any = null;
const readyPromise = newPromise(resolve => {
    // 把 resolve 保存起来
    readyResolve = resolve;
  });
const onReady = (cb: (...args: any[]) => void) => {
    readyPromise.then((...args: any[]) => {
      // resolve 执行完才会走 then
      // 才会执行回调函数
      cb(...args);
    });
  };

return {
    onReady,
    readyResolve,
  };
};

接口超时

typescript 复制代码
// 模拟实际请求
const fetchFn = (delay: number) => {
returnnewPromise<number>(resolve => {
    setTimeout(() => {
      resolve(1);
    }, delay);
  });
};

// 判断是否超时
const checkTimeout = (delay: number, timeout: number) => {
returnPromise.race([
    fetchFn(delay),
    newPromise((_, reject) => {
      setTimeout(() => {
        reject('超时啦!');
      }, timeout);
    }),
  ]);
};

// 没超时
checkTimeout(100, 500)
  .then(res => {
    console.log('没超时', res);
  })
  .catch(err => {
    console.log(err);
  });

// 超时了
checkTimeout(600, 500)
  .then(res => {
    console.log('没超时', res);
  })
  .catch(err => {
    console.log(err);
  });

控制并发

ini 复制代码
const limitFn = (limit) => {
const queue = [];
let activeCount = 0;

const next = () => {
    activeCount--;

    if (queue.length > 0) {
      queue.shift()();
    }
  };

const run = async (fn, resolve, ...args) => {
    activeCount++;

    const result = (async () => fn(...args))();


    try {
      const res = await result;
      resolve(res);
    } catch { }

    next();
  };

  const enqueue = (fn, resolve, ...args) => {
    queue.push(run.bind(null, fn, resolve, ...args));

    if (activeCount < limit && queue.length > 0) {
      queue.shift()();
    }
  };

const generator = (fn, ...args) =>
    newPromise((resolve) => {
      enqueue(fn, resolve, ...args);
    });

return generator;
};
相关推荐
用户21411832636027 分钟前
dify案例分享-deepseek赋能从 Excel 表格到统计图,一键生成代码不是梦
前端
Winwin8 分钟前
老帅 Webpack 和新将 Vite 的 PK
前端·前端工程化
best66614 分钟前
预检请求是什么?
前端
一袋米扛几楼9814 分钟前
【JavaScript 】1. 什么是 Node.js?(JavaScript 服务器环境)
服务器·javascript·node.js
前端加油站15 分钟前
单元测试入门与进阶
前端·单元测试
前端付杰20 分钟前
第八节: 全面理解vue3: 工具函数的核心作用与使用方法
前端·javascript·vue.js
Mr_sun.21 分钟前
Node.js与VUE安装
前端·vue.js·node.js
Tonychen22 分钟前
【React 源码阅读】为什么 React Hooks 不能用条件语句来执行?
前端·react.js·源码阅读
Cutey91625 分钟前
Vuex vs Pinia
前端·vue.js·面试
Sallywa31 分钟前
全局替换的思路历程
前端