在 Aurelia 和 TypeScript 中正确实施网络工作者

Proper implementation of web workers in Aurelia and TypeScript

遗憾的是,我在整个网络上找不到一个单一的答案。

我有一个基于 TypeScript / Aurelia CLI / RequireJS 的 Aurelia 应用程序。 结构如下:

|data
   |-MyService.ts
|workers
   |-SomeWorker.ts/js

存在一个名为 aurelia-pal-worker 的包,但没有文档或复杂的示例。


到目前为止我尝试了什么

只要我需要像 RxJs 这样的外部库,Browserify 方法就可以工作。 当然,当我尝试 require("../data/MyService.ts") 时,这会中断。为此,我需要用另一个替换整个构建管道,它使用 tsify 插件通过 Browserify 运行整个 aurelia 项目。

在我看来我有 3 个选择:

  • 找到一个工作示例,将 TypeScript 文件编译为网络工作者,并使用 aurelia-pal-worker 导入依赖项。
  • 使用 TypedWorker 并将昂贵的函数放入线程中,例如:
    new TypedWoker(expensiveFuncFromService, handleOutput)
  • 编译 MyService.ts 以分离 JS 文件(而不是捆绑它)并像这样要求它:
    require("/scripts/MyService.js")

后两个好像不太吸引我,但应该很简单。非常感谢任何提示或示例!

PS:对于任何不熟悉 Aurelia 的人:它在后台使用 gulp 管道。

所以经过一番摆弄后,我切换到 webpack based solution, which allows me to use the amazing webpack-worker-loader

这是修改我的现有项目并启动它和快速 运行 之间的最佳权衡。

最后是这样的:

custom_typings/worker-loader.d.ts

declare module "worker-loader!*" {
  const content: new () => any;
  export = content;
}

worker/some-service.ts

export class SomeService {
  public doStuff() {
    console.log("[SomeService] Stuff was done");
  }
}

worker/my-worker.ts

import "rxjs/add/observable/interval";

import { Observable } from "rxjs/Observable";
import { SomeService } from "./some-service";

const svc = new SomeService();
svc.doStuff();

console.log("[Worker] Did stuff");
onmessage = event => {
    console.log(event);
};

Observable.interval(1000).subscribe(x => postMessage(x));

worker加载后是这样的:

import * as MyWorker from "worker-loader!./worker/my-worker";
const worker = new MyWorker();
worker.onmessage = msg => console.log("[MyClass] got msg from worker", msg);

它将生成以下控制台输出:

1: "[SomeService] Stuff was done"
2: "[Worker] Did stuff"
3: "[MyClass] got msg from worker", 1
4: "[MyClass] got msg from worker", 2
...

您需要在 worker 中使用完整的 DI 吗?

不用担心,在 的一点帮助下,我想出了如何用我们基于 webpack 的解决方案重写它:

let container: Container = null;
let myService: SuperComplexService = null;

// Import the loader abstraction, so the DI container knows how to resolve our modules.
import("aurelia-pal-worker")
  .then(pal => pal.initialize())
  // We need some polyfills (like "Reflect.defineMetadata")
  .then(() => import("aurelia-polyfills"))
  // Then we get the DI service and create a container
  .then(() => import("aurelia-dependency-injection"))
  .then(({ Container }) => (container = new Container()))
  .then(() => import("../services/my-super-complex-service")) // Here we go!
  .then(({ SuperComplexService }) => (myService = container.get(SuperComplexService) as SuperComplexService))
  .then(() => startWorker());

const startWorker = async() => {
  // Let's get going!
}

此加载器链的所有功劳归于@jeremy-danyow。