实现一个异步排队执行的队列

101 阅读2分钟

异步排队执行队列

前言

有没有遇到这样一种情况:我需要执行很多个异步操作,但是我又希望这些异步操作是按照顺序依次执行,也就是上一次异步任务完成之后再执行下一次异步任务,如果有那么这篇文章将对你有帮助。友情提示:异步队列完整代码在文章末尾,中间为实现过程可以跳过

具体实现

首先需要一个存放所有异步方法的数组queue,一个可以像队列中添加新任务的push方法,最后需要有一个start函数来执行并清空这个queue

class AsyncQueue {
    queue = []
    push(task) {
        this.queue.push(task)
    }
	async start() {
        while(this.queue.length) {
            const task = this.queue.shift()
            await task()
        }
    }
}

start方法使用async/await处理,每次while循环会等上一次task执行完成再进入下一轮。这样就实现了异步队列雏形,接下来需要考虑一个问题:这个类的初衷是希望使用者仅需push任务而push进来的任务可以主动执行,所以我们需要在push的时候执行start

class AsyncQueue {
    queue = []
    running // 记录当前循环执行中的任务
    push(task) {
        this.queue.push(task)
        if (this.running) return
        this.running = this.start()
    }
	async start() {
        while(this.queue.length) {
            const task = this.queue.shift()
            await task()
        }
        this.running = undefined
    }
}

为了防止出现重复的while循环,使用running来记录当前正在循环中的任务,每次start之前判断是否正在循环执行如果有就直接返回。看起来就很完美了,设想一种情况同一时间有10个异步任务被push到队列中执行,但是我不希望让10个任务老老实实的排队执行这样太慢了,我只想让最后一次进入队列的任务正常执行呢。最简单的办法在push的时候直接将queue数组清空,这样在没有后续任务进来并到下一轮while循环时就会执行最后进来的任务,其实有点类似于debounce,下面是添加错误处理和debounce的代码。

错误处理和debounce

将push进来的task进行统一错误处理

class AsyncQueue {
    queue = []
    running // 记录当前循环执行中的任务
    debounce = false
    constructor({ debounce }) {
        this.debounce = debounce
    }
    push(task) {
        if(this.debounce) this.queue = []
        const { promise, reject, resolve } = Promise.withResolvers()
        this.queue.push(() => {
            task().then(resolve).catch(reject)
        })
        if (this.running) return
        this.running = this.start()
        return promise
    }
	async start() {
        while(this.queue.length) {
            const task = this.queue.shift()
            await task()
        }
        this.running = undefined
    }
}

Promise.widthResolvers是Promise的新api:developer.mozilla.org/zh-CN/docs/…

TS版

class AsyncQueue {
  queue: (() => Promise<any>)[] = [];
  running; // 记录当前循环执行中的任务
  debounce = false;
  constructor({ debounce }) {
    this.debounce = debounce;
  }

  push<T>(task: () => Promise<T>) {
    if (this.debounce) this.queue = [];
    let promise, reject, resolve;
    new Promise((r, j) => {
      (resolve = r), (reject = j);
    });
    this.queue.push(() => {
      return task().then(resolve).catch(reject);
    });
    if (this.running) return;
    this.running = this.start();
    return promise;
  }
  
  async start() {
    while (this.queue.length) {
      const task = this.queue.shift()!;
      await task();
    }
    this.running = undefined;
  }
}