Workers

Deno 支持 Web Worker API .

Worker 可以在多个线程中运行代码,Worker 的每个实例都会在单独的线程中运行,这个线程专属于 worker。

目前,Deno 只支持 module 类型的 worker,因此在创建新的 worker 时必须传递 type: "module" 选项。

Workers currently do not work in compiled executables .

Use of relative module specifiers in the main worker are only supported with --location <href> passed on the CLI. This is not recommended for portability. You can instead use the URL constructor and import.meta.url to easily create a specifier for some nearby script. Dedicated workers, however, have a location and this capability by default.

// Good
new Worker(new URL("./worker.js", import.meta.url).href, { type: "module" });

// Bad
new Worker(new URL("./worker.js", import.meta.url).href);
new Worker(new URL("./worker.js", import.meta.url).href, { type: "classic" });
new Worker("./worker.js", { type: "module" });

As with regular modules, you can use top-level await in worker modules. However, you should be careful to always register the message handler before the first await, since messages can be lost otherwise. This is not a bug in Deno, it's just an unfortunate interaction of features, and it also happens in all browsers that support module workers.

import { delay } from "https://deno.land/std@0.136.0/async/mod.ts";

// First await: waits for a second, then continues running the module.
await delay(1000);

// The message handler is only set after that 1s delay, so some of the messages
// that reached the worker during that second might have been fired when no
// handler was registered.
self.onmessage = (evt) => {
  console.log(evt.data);
};

Instantiation permissions

创建一个新的 Worker 实例的行为与动态导入类似,因此 Deno 需要适当的权限来做这个操作。

对于使用本地模块的 worker,Deno 需要读取 (--allow-read) 权限:

main.ts

new Worker(new URL("./worker.ts", import.meta.url).href, { type: "module" });

worker.ts

console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: read access to "./worker.ts", run again with the --allow-read flag

$ deno run --allow-read main.ts
hello world

对于使用远程模块的 worker,Deno 需要网络 (--allow-net) 权限:

main.ts

new Worker("https://example.com/worker.ts", { type: "module" });

worker.ts (at https ://example.com/worker.ts)

console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: net access to "https://example.com/worker.ts", run again with the --allow-net flag

$ deno run --allow-net main.ts
hello world

在 Worker 中使用 Deno

Starting in v1.22 the Deno namespace is available in worker scope by default. To enable the namespace in earlier versions pass deno: { namespace: true } when creating a new worker.

main.js

const worker = new Worker(new URL("./worker.js", import.meta.url).href, {
  type: "module",
});

worker.postMessage({ filename: "./log.txt" });

worker.js

self.onmessage = async (e) => {
  const { filename } = e.data;
  const text = await Deno.readTextFile(filename);
  console.log(text);
  self.close();
};

log.txt

hello world
$ deno run --allow-read main.js
hello world

Starting in v1.23 Deno.exit() no longer exits the process with the provided exit code. Instead is an alias to self.close(), which causes only the worker to shutdown. This better aligns with the Web platform, as there is no way in the browser for a worker to close the page.

Specifying worker permissions

这是一个不稳定的 Deno 特性。更多信息请查阅 不稳定特性

Worker 可用的权限类似于 CLI 权限标志,这意味着在那里启用的每个权限都可以在 Worker API 层面上被禁用。你可以在 这里 找到每个权限选项更详细的描述。

默认情况下,worker 将从其创建的线程中继承权限,但为了允许用户限制该 worker 的访问,我们在 worker API 中提供了 deno.permissions 选项。