美文网首页
js如何限制Promise“并发”的数量

js如何限制Promise“并发”的数量

作者: 指尖跳动 | 来源:发表于2020-04-13 11:36 被阅读0次

众所周知js是单线程,并不存在真正的并发,但是由于JavaScript的Event Loop机制,使得异步函数调用有了“并发”这样的假象。这里只是形象说明才这么称呼的,因此用了引号。

它的使用场景如限制网络请求的数量,限制文件下载请求的上限等等。开发过微信小程序的都知道,网络请求wx.request、wx.downloadFile等接口的最大并发限制是10。

那么我们如何实现这样的功能,让我们可以随意调用受限制的函数,而又不需要当心它是否超过了限制。

这里依然可以利用到任务队列这种思想,在每次要执行“受限”任务时,判断当前正在执行的任务数量是否超过给定的上限,如果未超过则立即执行这个“任务”,否则进入任务队列中等待执行。

由于我们经常使用Promise作为异步编程的解决方案,这里把异步任务封装成一个Promise或者async函数。

class LimitPromise {
  constructor (max) {
    // 异步任务“并发”上限
    this._max = max
    // 当前正在执行的任务数量
    this._count = 0
    // 等待执行的任务队列
    this._taskQueue = []
  }

  /**
   * 调用器,将异步任务函数和它的参数传入
   * @param caller 异步任务函数,它必须是async函数或者返回Promise的函数
   * @param args 异步任务函数的参数列表
   * @returns {Promise<unknown>} 返回一个新的Promise
   */
  call (caller, ...args) {
    return new Promise((resolve, reject) => {
      const task = this._createTask(caller, args, resolve, reject)
      if (this._count >= this._max) {
        // console.log('count >= max, push a task to queue')
        this._taskQueue.push(task)
      } else {
        task()
      }
    })
  }

  /**
   * 创建一个任务
   * @param caller 实际执行的函数
   * @param args 执行函数的参数
   * @param resolve
   * @param reject
   * @returns {Function} 返回一个任务函数
   * @private
   */
  _createTask (caller, args, resolve, reject) {
    return () => {
      // 实际上是在这里调用了异步任务,并将异步任务的返回(resolve和reject)抛给了上层
      caller(...args)
        .then(resolve)
        .catch(reject)
        .finally(() => {
          // 任务队列的消费区,利用Promise的finally方法,在异步任务结束后,取出下一个任务执行
          this._count--
          if (this._taskQueue.length) {
            // console.log('a task run over, pop a task to run')
            let task = this._taskQueue.shift()
            task()
          } else {
            // console.log('task count = ', count)
          }
        })
      this._count++
      // console.log('task run , task count = ', count)
    }
  }
}

上述代码内容很少,主要的核心函数也就两个。

  • 调用器:就是把真正的执行函数和参数传入,创建返回一个新的Promise,而这个新Promise的什么时候返回,取决于这个异步任务何时被调度。Promise内部主要就是创建一个任务,判断任务是执行还是入队。
  • 创建任务:实际上就是返回了一个函数,将真正的执行函数放在里面执行。这里利用了Promise的finally方法,在finally中判断是否执行下一个任务,实现任务队列连续消费的地方就是这里。

下面举个例子怎么使用它。假设我们有一个网络请求模块,叫request.js,包含get和post方法,一般情况下,是这样使用的:

const request = require('./request')
request.get('https://www.baidu.com')
  .then((res) => {
    // 处理返回结果
  })
  .catch(err => {
    // 处理异常情况
  })

现在我们要把它改造成受限制的网络请求,假设请求上限设为10个,并起名叫limitRequest.js。实现如下:

const LimitPromise = require('limit-promise')
const request = require('./request')
// 请求上限
const MAX = 10
// 核心控制器
const limitP = new LimitPromise(MAX)

// 利用核心控制器包装request中的函数
function get (url, params) {
  return limitP.call(request.get, url, params)
}
function post (url, params) {
  return limitP.call(request.post, url, params)
}
// 导出
module.exports = {get, post}

这里就完成受限请求模块的构建了,是不是很简单,而且调用接口完全没变,只需要引入limitRequest.js替代原先的即可。

代码已上传至GitHub:limit-promise


from: https://www.jianshu.com/p/cc706239c7ef

相关文章

  • js如何限制Promise“并发”的数量

    GitHub地址:limit-promise 众所周知js是单线程,并不存在真正的并发,但是由于JavaScrip...

  • js如何限制Promise“并发”的数量

    众所周知js是单线程,并不存在真正的并发,但是由于JavaScript的Event Loop机制,使得异步函数调用...

  • JAVA并发编程

    [TOC] Semaphore 此类的主要作用就是限制线程并发的数量,如果不限制并发的数量,则CPU的资源很快就被...

  • JAVA控制线程并发数量

    JAVA控制线程并发数量方法主要有,限制一个线程使用(synchronized),限制指定数量的线程并发使用(Se...

  • Nginx 限制ip并发数及请求速度

    1. 限制单IP并发访问数量 nginx中ngx_http_limit_conn_module模块用于限制连接数量...

  • Celery #3 并发

    Celery的任务,默认是并发执行(开启Pool)的。但有并发数量限制。并发数达到上限前,celery会不停接收任...

  • 八(1)、Promise(周) ------ 2020-02-2

    1、什么是Promise: 2、通过原生JS实现简单的Promise原理,理解Promise如何管控异步编程: 3...

  • fetch vs axios

    axios 从 node.js 创建 http 请求。 支持 Promise API。 提供了一些并发请求的接口(...

  • JS_Promise底层原理封装

    JS_Promise底层原理封装 封装的难点在于如何让promise中的.then()变成一个微任务 ? 如何实现...

  • OC如何实现Promise

    JS中Promise和Ramda中的curry化函数,真的很好用。我就在想: 如何在OC中实现Promise 如何...

网友评论

      本文标题:js如何限制Promise“并发”的数量

      本文链接:https://www.haomeiwen.com/subject/eekgmhtx.html