-
Notifications
You must be signed in to change notification settings - Fork 390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generator functions #435
Comments
|
Hello, I could use generators by defining a function that returns a proxy of a generator. // worker.ts
const fibGen = function* (max: number) {
if (max > 0) yield 1
if (max > 1) yield 1
let a = 1, b = 1
for (let i = 2; i < max; i++) {
const c = a
a = b
b = a + c
yield b
}
}
// returns a proxy
const fibGen2 = (max: number) => Comlink.proxy(fibGen(max))
Comlink.expose({ fibGen: fibGen2 })// main.js
const wrapped = Comlink.wrap(new Worker("./worker.js"))
const gen = await wrapped.fibGen(50)
// `for await ... of` statements implicity call the method `[Symbol.asyncIterator]()` of a object.
// However, `gen[Symbol.asyncIterator]()` returns not AsyncGenerator but Promise<AsyncGenerator>
// Therefore you should wrap gen in { [Symbol.asyncIterator]() { ... } }
const gen2 = { [Symbol.asyncIterator]() { return gen } }
for await (const i of gen2) {
console.log(i) // 1, 2, 3, 5, ...
} |
|
I ended up doing something similar to this but written as a handler: // transferHandlers.ts
import {transferHandlers, proxy} from 'comlink';
transferHandlers.set("iterable", {
canHandle(obj) {
const isIterable = typeof obj === "object" && !Array.isArray(obj) && (Symbol.iterator in obj || Symbol.asyncIterator in obj)
return isIterable
},
deserialize(obj) {
return new Proxy(transferHandlers.get("proxy")!.deserialize(obj), {
get(target, key) {
if(key === Symbol.asyncIterator) return async function*() {
let a;
do {
a = await target.next()
yield a.value
} while(!a.done)
}
else return target[key]
},
has(target, key) {
if(key === Symbol.asyncIterator) return true
else return key in target
}
})
},
serialize(obj) {
// Is in main thread?
return transferHandlers.get("proxy")!.serialize(proxy(obj))
}
})I just made sure to import this file in the main and worker contexts, and I could export generators without any additional boilerplate. |
|
Older versions of Comlink supported generators by turning them into async generators, but support for async generators was lacking at the time. I should revisit this now. |
|
@surma, this was my first attempt at creating a transfer handler, do you see any issues with my implementation? Big fan of your work! |
|
@xepher0coded I saw another approach by @samburnstone the other day. I haven't compared and contrasted the approaches yet, but I'm probably going to be doing this myself soon and figured I'd link to it since others would probably like to see different approaches in-the-wild. |
|
Hi @sebinsua - well found! I meant to post here but ended up being distracted by other things. Looking over the handler provided by @xepher0coded, that seems a much better solution as it's reusing the proxy transfer handler rather than essentially duplicating it like I do (although I found it a useful exercise to better understand how Comlink was using message channels to pass values back and forth). It also looks like it allows for other properties to be called directly on the method, whereas my solution simply supports the iterator properties ( The only thing I spotted is when manually calling |
|
(previously @xepher0coded, changed my username)
|
|
I also had the issue @samburnstone mentioned with the proxied async generator iterating one additional time after it returns (with an undefined value). @egriff38 I wasn't quite sure what you meant by setting the However, I was able to fix the issue with the extra iteration by including an additional check for This is what I came up with: // comlink-transfer-handler-iterable.js
import {transferHandlers, proxy} from 'comlink';
transferHandlers.set("iterable", {
canHandle: (obj) => {
const isIterable = typeof obj === "object" && !Array.isArray(obj) &&
(Symbol.iterator in obj || Symbol.asyncIterator in obj);
return isIterable;
},
deserialize: (obj) => {
return new Proxy(transferHandlers.get("proxy").deserialize(obj), {
get: (target, prop) => {
if(prop === Symbol.asyncIterator) {
const gen = async function*() {
while (true) {
const nextObj = await target.next();
if (nextObj.done) {
return nextObj.value;
}
yield nextObj.value;
}
};
return gen;
}
else return Reflect.get(...arguments)
},
has: (target, prop) => {
if(prop === Symbol.asyncIterator) return true
else return prop in target
}
})
},
serialize: (obj) => {
return transferHandlers.get("proxy").serialize(proxy(obj));
},
}) |
|
Hey! Is there any update on this issue? We have quite a big project, and it is not easy to change the syntax of functions. I believe our issue is similar, where the Unable to run initialize due to |
|
I think the transfer handler can be simplified. |
Hello!
I am a new user to comlink and am thrilled to begin integrating it into my projects as the primary way to communicate between the main thread and web workers. My question is, does comlink provide any way of working with generator functions?
My base example is as follows:
I know there are other API methods available with Comlink, I'm just a little green on the best way to go about using them in this case.
Thanks in advance! This is my fourth stopping point after workerize-loader, workerize, and comlink-loader, and has been by far the easiest implementation with my existing setup.
The text was updated successfully, but these errors were encountered: