首页 > 其他分享 >解决Promise的多并发问题

解决Promise的多并发问题

时间:2023-09-19 10:46:03浏览次数:32  
标签:fetchFn const queue 并发 resolve Promise 解决

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

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

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

 

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

 

代码实现

实现思路

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

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

 

Promise.all

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

const fetchFn = (delay, index) => {
  return new Promise(resolve => {
    console.log(index)
    setTimeout(() => {
      resolve(index)
    }, delay);
  })
}


const promises = [
  fetchFn(1000, 1),
  fetchFn(1000, 2),
  fetchFn(1000, 3),
  fetchFn(1000, 4),
  fetchFn(1000, 5),
  fetchFn(1000, 6)
]

Promise.all(promises)

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

 

所以我们需要做一些改造,让Promise.all执行 promises 时支持控制并发,但是我们改造的不应该是Promise.all,而是这一个个的fetchFn

期望效果

const limitFn = (limit) => {
  // ...coding
}

// 最大并发数 2
const generator = limitFn(2)


const promises = [
  generator(() => fetchFn(1000, 1)),
  generator(() => fetchFn(1000, 2)),
  generator(() => fetchFn(1000, 3)),
  generator(() => fetchFn(1000, 4)),
  generator(() => fetchFn(1000, 5)),
  generator(() => fetchFn(1000, 6))
]

Promise.all(promises)

 

 

 

实现 limitFn

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

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

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

const limitFn = (concurrency) => {
  const queue = [];
  let activeCount = 0;

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

  return generator;
};

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

  • 将每一个 fetchFn 放进队列里
  • 将坑位里的 fetchFn 先执行
const enqueue = (fn, resolve, ...args) => {
  queue.push(run.bind(null, fn, resolve, ...args));

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

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

  if (activeCount < limit && queue.length > 0) {
    queue.shift()(); // 这段代码
  }

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

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

  • 1、将 activeCount++ ,这时候执行中的并发数 +1
  • 2、将 fetchFn 执行,并把结果 resolve 出去,说明这个并发执行完了
  • 3、将 activeCount--,这时候执行中的并发数 -1
  • 4、从 queue 中取一个并发,拿来补坑执行
const run = async (fn, resolve, ...args) => {
  activeCount++;

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


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

  next();
};

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

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

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

完整代码

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) =>
    new Promise((resolve) => {
      enqueue(fn, resolve, ...args);
    });

  return generator;
};

这不是我写的

其实这是一个很出名的库的源码,就是p-limit,哈哈,但是重要吗?知识嘛,读懂了,它就是你的,到时跟面试官唠嗑的时候,他哪知道是不是真的是你写的~

 

标签:fetchFn,const,queue,并发,resolve,Promise,解决
From: https://www.cnblogs.com/houxianzhou/p/17713990.html

相关文章

  • npm does not support Node.js的解决办法
    原文链接:https://blog.csdn.net/UKilll/article/details/131589827 1、查看npm和nodejs的版本是否配套。2、因为我的使用了npminstall-gnpm把npm的版本升级到了9.几导致运行项目的时候提示npmv9.8.0doesnotsupportNode.jsv14.16.1,查阅了很多资料之后感觉太麻烦了,现......
  • 路由器中继解决多年来房间信号差的问题
    最近这段时间,发现书房的WIFI网络很差,5格信号只有3格有效,联想到平时在主卧房间和卫生间上网时基本没WIFI信号,是时候要想个办法解决一下了。经过一番研究后,最终通过一台闲置的路由器解决了以上问题。首先是我在网上查到了一款小米无线信号放大器,为了了解这块产品是否好用,我打开抖音......
  • VC6.0编译器中混有.c文件时出现fatal error C1853错误解决办法及extern "C"说明
    第一章的sample1,文中提到由于windows底层代码基本上是用c语言编写的,因此新工程里的CPP文件要改为C文件。但是在编译时出现错误fatalerrorC1853:"debug/1_1.pch"isnotaprecompliedheaderfilewiththiscomplier......这个问题还真是头一次遇到,怎么办?百度一下,解决办法......
  • Java并发Map的面试指南:线程安全数据结构的奥秘
    简介在计算机软件开发的世界里,多线程编程是一个重要且令人兴奋的领域。然而,与其引人入胜的潜力相伴而来的是复杂性和挑战,其中之一就是处理共享数据。当多个线程同时访问和修改共享数据时,很容易出现各种问题,如竞态条件和数据不一致性。本文将探讨如何在Java中有效地应对这些挑战,介......
  • Java并发Map的面试指南:线程安全数据结构的奥秘
    简介在计算机软件开发的世界里,多线程编程是一个重要且令人兴奋的领域。然而,与其引人入胜的潜力相伴而来的是复杂性和挑战,其中之一就是处理共享数据。当多个线程同时访问和修改共享数据时,很容易出现各种问题,如竞态条件和数据不一致性。本文将探讨如何在Java中有效地应对这些挑战,......
  • 解决Visual Studio 2022中无法编译 .NET Framework 4.5/4.5.1项目(Visual Studio 2022
    最新【一键处理】方法:https://github.com/MrXhh/VSTools/releases1)下载VS2022Net4NotCompileFix2)右键管理员执行3)重启VS https://github.com/MrXhh/VSTools/releases......
  • 巧用枚举解决业务场景的 Switch 语句—Java 实践
    巧用枚举解决业务场景的Switch语句——Java实践Switch语句是一种常见的流程控制语句,用于根据不同的条件执行不同的代码块。然而,当业务场景变得复杂时,使用大量的Switch语句可能导致代码冗长、难以维护和扩展。本文将介绍如何巧妙地使用枚举类型来优化和简化Switch语句的使......
  • c# winform打开外部程序异常问题解决方案
    c#winform中打开外部程序的常规操作是使用Process类,此时,如果外部程序没有对路径的操作或其他路径文件的操作时,通常不会出现报错或异常;反之,会出现找不到路径或者直接抛出异常。此种情况主要是因为外部程序和当前程序不在一个路径下导致的,以下是解决方案:System.IO.Directory.Set......
  • 智慧校园电子班牌解决方案
    智慧校园电子班牌系统全套源码 智慧电子班牌主要应用于班级门口,借助移动化管理工具和交互智能设备,以德育宣传、家校互通、教改走班为切入点,实现家校留言、文化宣传、通知公告、请假管理、班级信息展示、课程课表、作业通知、德育宣传、多种识别方式、考勤统计、选课走班、问卷调查......
  • pytest+allure+jenkins+python+git,实现Linux服务器看测试报告,并发送邮件通知
    1、简介pytest:一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。allure:是开源测试报告框架。jenkins:持续集成工具python:测试脚本语言git:开源的分布式版本控制系统2、Windows端我们需要开发测试代码,使用git上传到云端仓库2.1......