Comlink
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.icon
APIは3つ
Comlink.proxy(endpoint)
Returns the value that is exposed on the other side of endpoint.
通常clientでworker.onmessageを仕掛けているworkerをendpointに与えればいい daiiz.icon
Comlink.expose(obj, endpoint)
Exposes obj to endpoint.
Use Comlink.proxy on the other end of endpoint.
Web Workerで使うなら大抵endpointはself daiiz.icon
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):
Comlink.proxy に渡っているのは https://gyakky.herokuapp.com/svgyazo/9628e85e5af77c4163e468e0e11f2c13.svg
code:client.js
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();
};
code:worker.js
class MyClass {
constructor(init = 0) {
this._counter = init;
}
get counter() {
return this._counter;
}
increment(delta = 1) {
this._counter += delta;
}
}
Comlink.expose(MyClass, self);
exampleシンプルでよい
client側
Comlink.proxy(port2) に渡しているのはhttps://gyakky.herokuapp.com/svgyazo/476eafaf9a9ea567fd0a6e668d40cd5f.svg
code:client.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側
code:worker.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.icon
importScripts
ES6 Proxy