如何围绕 Web Workers API 编写 Promise 包装器?

How to write a Promise wrapper around Web Workers API?

我正在编写一个使用 Web Worker 的库。库的使用者不应该看到任何 Web Worker 的东西,而应该从这个库的 public 方法中获得 returned 一个 Promise,如下所示:

// consumer.js

const api = new Api();

api.doCalculation(input1).then(data => console.log(data));
api.doCalculation(input2).then(data => console.log(data));
api.doCalculation(input3).then(data => console.log(data));

在我的库代码中,我有一个 class 包装了 Web Worker 逻辑。在构造函数中,我创建了工作线程并设置了 "message" 事件监听器,监听来自工作线程的传入数据。 在这个 class 中还有一个 doCalculation(input) 方法,它对库的使用者来说是 public 。它获取输入并将其发送到工作线程以执行实际计算。

// api.js

class Api {
  constructor() {
    this.worker = new Worker('worker.js');

    this.worker.addEventListener('message', (e) => {

      // I want to return this e.data from the doCalculation method
      console.log(e.data);
    });
  }

  doCalculation(input) {
    this.worker.postMessage({input: input});

    // I want to return a Promise from this method,
    // holding the result e.data
  }
}

我现在的问题是,我如何 return 来自 doCalculation 方法的 Promise 持有 e.data

我最初的打算是这样的,但显然行不通,因为每次调用 doCalculation.

都会创建一个新的 "message" 事件侦听器
// api.js

class Api {
  constructor() {
    this.worker = new Worker('worker.js');
  }

  doCalculation(input) {
    this.worker.postMessage({input: input});
    return new Promise((resolve => {
      this.worker.addEventListener('message', (e) => {
        resolve(e.data);
      });
    }))
  }
}

这里的所有代码示例都经过简化,只是为了阐明我的观点。

如能提供正确方向的任何提示,我将不胜感激!

当然可以将 resolve 存储在某处,例如在对象中:

 this.resolvers = {};
 this.count = 0; // used later to generate unique ids

然后为每个发送给 webworker 的任务创建一个唯一的 id,并将 promise 解析器存储在那里

 const id = this.count++;
 // Send id and task to WebWorker
 return new Promise(resolve => this.resolvers[id] = resolve);

然后当webworker发送消息时,从中获取id,并解析存储的promise:

 this.resolvers[ id ](data);
 delete this.resolvers[id]; // Prevent memory leak

这样 (1) 您只需要注册一个处理程序,(2) webworker 可以同时处理多个任务,并且 (3) 您可以轻松确定哪些任务 运行 webworker 通过检查 Object.keys(this.resolvers).