Comlink

Comlink

GoogleChromeLabsにあった

Comlink makes WebWorkers enjoyable.

Comlink’s goal is to make WebWorkers enjoyable.
Comlink removes the mental barrier of thinking about postMessage and hides the fact that you are working with workers.
postMessageの心理的障壁をなくしたい、というのわかる daiiz

最終的には https://github.com/GoogleChromeLabs/clooney のような抽象ライブラリを作りたいらしい

APIは3つ
Comlink.proxy(endpoint)
Returns the value that is exposed on the other side of endpoint .
通常clientで worker.onmessage を仕掛けている worker をendpointに与えればいい daiiz
Comlink.expose(obj, endpoint)
Exposes obj to endpoint .
Use Comlink.proxy on the other end of endpoint .
Web Workerで使うなら大抵endpointはself daiiz
Comlink.proxyValue(value)
デフォルトではdeepCopyされるところ、参照渡ししてくれる
Makes sure a parameter or return value is proxied, not copied.
By default, all parameters to a function that are not transferable are copied (structural clone):

ES6 Proxyを使っている

WebWorkerに対して使う例
Comlink.proxy に渡っているのは
client.js
Copied!
async function init() {
// proxyの引数には'onmessage'を持つobjectを与える
const MyClass = Comlink.proxy(new Worker('worker.js'));
instance1 = await new MyClass();
instance2 = await new MyClass(42);
await showState();
await instance1.increment();
await instance2.increment(23);
await showState();
};

worker.js
Copied!
importScripts("https://cdn.jsdelivr.net/npm/comlinkjs@3/umd/comlink.js");

class MyClass {
constructor(init = 0) {
this._counter = init;
}
get counter() {
return this._counter;
}
increment(delta = 1) {
this._counter += delta;
}
}
Comlink.expose(MyClass, self);


ServiceWorkerに対しても使えるらしい
JavaScript Workerの一種だし、これもpostMessage生えているもんな
exampleシンプルでよい
serviceworker-studyと合わせて読むと勉強になる daiiz

client側
Comlink.proxy(port2) に渡しているのは
client.js
Copied!
import * as Comlink from "https://cdn.jsdelivr.net/npm/comlinkjs@3/comlink.js";
async function initComlink () {
const {port1, port2} = new MessageChannel();
const msg = {
comlinkInit: true,
port: port1
};
navigator.serviceWorker.controller.postMessage(msg, [port1]); // 相手
const swProxy = Comlink.proxy(port2); // 自分
console.log(await swProxy.counter);
await swProxy.inc();
console.log(await swProxy.counter);
}
if (navigator.serviceWorker.controller) {
initComlink();
}
navigator.serviceWorker.addEventListener('controllerchange', initComlink);
navigator.serviceWorker.register("worker.js");

ServiceWorker側
worker.js
Copied!
importScripts("https://cdn.jsdelivr.net/npm/comlinkjs@3/umd/comlink.js");

addEventListener("install", () => skipWaiting());
addEventListener("activate", () => clients.claim());

// SW側にstate持つと管理できないのでよくないという話を聞いたが、これはデモなので良い?
const obj = {
counter: 0,
inc() {
this.counter++;
}
};

self.addEventListener("message", event => {
if (event.data.comlinkInit) {
Comlink.expose(obj, event.data.port);
return;
}
});

キャッチアップできてないので勉強すべき daiiz
MessageChannel
importScripts
ES6 Proxy
Powered by Helpfeel