Comments (6)
Right, so currently it’s the user’s responsibility to make sure the worker is ready before using Comlink. Because you create the worker in the click hander (side-note: generally I wouldn’t recommend that!), you call getNumberPlusOne()
before the worker is ready. So the message that tells the worker that the function was called just gets lost.
You either need to send a ”ready!” message and wait for that first or — the more fragile, hackier and easier solution — is to move the worker creation to the top of the file. Unless the user clicks the button in under 2 seconds (because of your setTimeout
) it works! See here.
This is something I’d like to address in an update to Comlink where wrap()
returns a Promise that you have to await where Comlink waits until the worker calls expose()
.
from comlink.
I wrote myself thin wrappers with an EventListener that waits for a simple { ready: true }
message from the Worker before resolving a Promise with the Comlink-wrapped Remote. This probably isn't ripe for a pull request but might serve as inspiration to those that encounter this issue right now.
The environment consists of Vue 3.3.4
with TypeScript 5.0.4
and Vite 4.3.9
.
// @/worker/spawn.ts
import { wrap as comlink, type Remote } from "comlink";
/** An object with both the original Worker and the Comlink-wrapped Remote of it. */
export type RemoteWorker<T> = { worker: Worker, remote: Remote<T> };
/** The message expected by the `readinessListener`. */
export const Ready = { ready: true };
/** Listen for the readiness message from the Worker and call the `callback` once. */
export function readinessListener(worker: Worker, callback: () => void) {
worker.addEventListener("message", function ready(event: MessageEvent<typeof Ready>) {
if (!!event.data && event.data.ready === true) {
worker.removeEventListener("message", ready);
callback();
};
});
};
/** Create a new Comlink-wrapped Worker of type `T` and attach an optional
* readiness-callback to signal when it's ready to receive requests. **/
export function spawnSync<T>(path: URL | string, ready?: () => void): RemoteWorker<T> {
// create a new worker using the Vite API
// https://vitejs.dev/guide/features.html#web-workers
let worker = new Worker(new URL(path, /* relative to _this_ file */ import.meta.url), { type: "module" });
// optionally attach the callback using readiness listener
if (ready != undefined) readinessListener(worker, ready);
// wrap worker with comlink
let remote = comlink<T>(worker);
return { worker, remote };
};
/** Create a new Comlink-wrapped Worker of type `T` asynchronously. The Promise
* resolves only once the Remote is ready to receive messages. **/
export async function spawn<T>(path: URL | string): Promise<RemoteWorker<T>> {
// create a new worker using the Vite API
// https://vitejs.dev/guide/features.html#web-workers
let worker = new Worker(new URL(path, /* relative to _this_ file */ import.meta.url), { type: "module" });
// create a promise to wait for the readiness message
await new Promise<void>(resolve => readinessListener(worker, resolve));
// now we're ready to wrap the worker with comlink
let remote = comlink<T>(worker);
return { worker, remote };
};
Then expose the Worker like this:
// @/worker/simpleworker.ts
import { expose } from "comlink";
import { spawn, Ready } from "./spawn";
export class SimpleWorker {
// ...
}
expose(new SimpleWorker());
postMessage(Ready); // signal the readinessListener
And instantiate it like this:
import { spawn, spawnSync } from "@/worker/spawn";
import { SimpleWorker } from "@/worker/simpleworker";
// asynchronously with promise
const simple = await spawn<SimpleWorker>("simpleworker");
console.log("SimpleWorker is ready!");
// synchronously with callback
const simple = spawnSync<SimpleWorker>("simpleworker", () => console.log("SimpleWorker is ready!"));
from comlink.
Just had the same issue with vue and creating and setting up a worker in the setup section of my component. Here is the above solution in Javascript.
async function wrap(worker) {
await new Promise((resolve, reject) => {
const controller = new AbortController();
worker.addEventListener('message', (message) => {
if (message?.data?.ready) {
controller.abort();
resolve();
}
}, { signal: controller.signal })
})
return Comlink.wrap(worker);
}
In your worker:
Comlink.expose(object);
postMessage({ready: true})
And to use it:
import Worker from './worker?worker'
await wrap(new Worker());
from comlink.
I had the exact same issue. I think that's because of how import
is implemented for the worker.
from comlink.
This is something I’d like to address in an update to Comlink where
wrap()
returns a Promise that you have to await where Comlink waits until the worker callsexpose()
.
I'd love that! I'm just beginning to use comlink-wrapped Workers in a Vue component and was wondering why the very simple ready() { return true; }
function simply seemed to hang. Because the top-level <script setup>
block in single-file components does not allow asynchronous functions, I've put the calls in the onMounted
lifecycle hook. But the Promises only started resolving after I inserted a delay of several hundred milliseconds.
[edit: this only happens when I initialize more than one single Worker in a single component <script setup>
though. If there's only one it seems to be quick enough.]
If the Comlink.wrap()
function returns a Promise, I won't be able to use it in the <script setup>
block but maybe I'm better off putting it all outside a component anyway. At least then I'll have a sure way of knowing the link is ready. 😊
from comlink.
Any repro Vue example?
from comlink.
Related Issues (20)
- Missing types for `Comlink.finalizer` props on objects. HOT 4
- Possible to get reference to original object from a proxy? HOT 2
- Proxy not inferred as a promise when it's a nested inside a proxied object as a property value HOT 4
- Service worker example: stops working when SW is suspended HOT 9
- Set operation is not awaitable HOT 2
- Possible to communicate between web workers? HOT 1
- How to transfer result buffers HOT 2
- [feat] DeasyncEndpoint HOT 2
- Move from Karma to Playwright? HOT 1
- Do I need to use transfer inside a proxy? HOT 1
- Add support for async transferHandle serializer/deserializer HOT 2
- Significant performance optimizations possible in `requestResponseMessage` HOT 4
- Push notifications from web worker HOT 5
- Memory leak when terminating worker with ongoing call HOT 2
- Worker Pool for similar tasks HOT 1
- Sharing constructed objects within a remote context
- How to implement comlink in Next.js 14 App Route
- Feature request: Automatic proxy of arguments HOT 3
- Having issues when using comlink with react (storing in react state) HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from comlink.