简体   繁体   English

如何在 JS/TS 中实现伪阻塞异步队列?

[英]How to implement a pseudo blocking async queue in JS/TS?

So here's an oxymoron: I want to create an asynchronous blocking queue in javascript/typescript (if you can implement it without typescript, that's fine).所以这是一个矛盾的说法:我想在 javascript/typescript 中创建一个异步阻塞队列(如果你可以在没有 typescript 的情况下实现它,那很好)。 Basically I want to implement something like Java's BlockingQueue expect instead of it actually being blocking, it would be async and I can await dequeues.基本上我想实现像 Java 的BlockingQueue期望的东西而不是它实际上是阻塞的,它是异步的,我可以等待出队。

Here's the interface I want to implement:这是我要实现的接口:

interface AsyncBlockingQueue<T> {
  enqueue(t: T): void;
  dequeue(): Promise<T>;
}

And I'd use it like so:我会像这样使用它:

// enqueue stuff somewhere else

async function useBlockingQueue() {
  // as soon as something is enqueued, the promise will be resolved:
  const value = await asyncBlockingQueue.dequeue();
  // this will cause it to await for a second value
  const secondValue = await asyncBlockingQueue.dequeue();
}

Any ideas?有什么想法吗?

It's quite simple actually, dequeue will create a promise that enqueue will resolve.其实很简单, dequeue会创建一个承诺,入enqueue会解决。 We just have to keep the resolvers in a queue - and also care about the case where values are enqueued before they are dequeued, keeping the already fulfilled promises in a queue.我们只需要将解析器保持在队列中 - 并且还关心值在出队之前入队的情况,将已经履行的承诺保持在队列中。

class AsyncBlockingQueue {
  constructor() {
    // invariant: at least one of the arrays is empty
    this.resolvers = [];
    this.promises = [];
  }
  _add() {
    this.promises.push(new Promise(resolve => {
      this.resolvers.push(resolve);
    });
  }
  enqueue(t) {
    // if (this.resolvers.length) this.resolvers.shift()(t);
    // else this.promises.push(Promise.resolve(t));
    if (!this.resolvers.length) this._add();
    this.resolvers.shift()(t);
  }
  dequeue() {
    if (!this.promises.length) this._add();
    return this.promises.shift();
  }
  // now some utilities:
  isEmpty() { // there are no values available
    return !this.promises.length; // this.length <= 0
  }
  isBlocked() { // it's waiting for values
    return !!this.resolvers.length; // this.length < 0
  }
  get length() {
    return this.promises.length - this.resolvers.length;
  }
  [Symbol.asyncIterator]() {
    return {
      next: () => this.dequeue().then(value => ({done: false, value}))
    };
  }
}

I don't know TypeScript, but presumably it's simple to add the the necessary type annotations.我不知道 TypeScript,但大概添加必要的类型注释很简单。

For better performance, use a Queue implementation with circular buffers instead of plain arrays, eg this one .为了获得更好的性能,请使用带有循环缓冲区的 Queue 实现而不是普通数组,例如this one You might also use only a single queue and remember whether you currently store promises or resolvers.您也可能只使用一个队列并记住您当前是存储承诺还是解析器。

This is simply @Bergi's answer but with typescript + generics with some modifications to make it work with strict mode for my typescript peeps out there.这只是@Bergi 的答案,但是使用 typescript + generics 进行了一些修改,以使其在严格模式下工作,以便我的 typescript 窥视。

class AsyncBlockingQueue<T> {
  private _promises: Promise<T>[];
  private _resolvers: ((t: T) => void)[];

  constructor() {
    this._resolvers = [];
    this._promises = [];
  }

  private _add() {
    this._promises.push(new Promise(resolve => {
      this._resolvers.push(resolve);
    }));
  }

  enqueue(t: T) {
    if (!this._resolvers.length) this._add();
    const resolve = this._resolvers.shift()!;
    resolve(t);
  }

  dequeue() {
    if (!this._promises.length) this._add();
    const promise = this._promises.shift()!;
    return promise;
  }

  isEmpty() {
    return !this._promises.length;
  }

  isBlocked() {
    return !!this._resolvers.length;
  }

  get length() {
    return this._promises.length - this._resolvers.length;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM