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の心理的障壁をなくしたい、というのわかる 
最終的には 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に与えればいい Comlink.expose(obj, endpoint)
Exposes
obj
to endpoint
. Use Comlink.proxy on the other end of
endpoint
. Web Workerで使うなら大抵endpointはself 
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
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
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と合わせて読むと勉強になる 
client側
Comlink.proxy(port2) に渡しているのは
client.js
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
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;
}
});
キャッチアップできてないので勉強すべき 
MessageChannel
importScripts
ES6 Proxy