threads.js icon indicating copy to clipboard operation
threads.js copied to clipboard

Support (async) generator functions

Open kimamula opened this issue 5 years ago • 2 comments
trafficstars

I think it would be great if the observer pattern described here can be written as follows so that we do not have to depend on any observable libraries but ECMAScript standards.

// master.js
import { spawn, Thread, Worker } from "threads"

const counter = await spawn(new Worker("./workers/counter"))

for await (const newCount of counter()) {
  console.log(`Counter incremented to:`, newCount)
}
// workers/counter.js
import { expose } from "threads/worker"

function* startCounting() { // async generator function should also be OK
  for (let currentCount = 1; currentCount <= 10; currentCount++) {
    yield currentCount;
  }
}

expose(startCounting)

kimamula avatar May 27 '20 03:05 kimamula

Hey @kimamula!

Thanks for bringing that up. I'd like to see this as two related topics here:

  1. Supporting async iterators as return values of functions:

    Makes sense 👍

  2. Replacing observables with them:

    That's not gonna work that way as they serve roughly similar, but not the same use cases. Observables can actively raise an event and inform the subscriber about them at any time. Asynchronous iterators as returned by async generator functions, however, require the subscribing code the call them and might just not yield the event immediately. The subscriber needs to actively ask for new data, whereas the observable yields new data without any action of the subscribing code.

    So bottom line one is still a generator, even though async, so you tell it to generate a new value and after some time it will have done so, whereas the observable has a lifecycle of its own and will tell you when there's new data 😉

andywer avatar May 27 '20 07:05 andywer

That makes sense and therefore passing value from a subscriber with next() should be possible in this pattern.

// master.js
import { spawn, Worker } from "threads"

const counter = (await spawn(new Worker("./workers/counter")))()

console.log(await counter.next()) // {value: 1, done: false}
console.log(await counter.next()) // {value: 2, done: false}
console.log(await counter.next()) // {value: 3, done: false}
console.log(await counter.next(true)) // {value: 1, done: false}
console.log(await counter.next()) // {value: 2, done: false}
console.log(await counter.next()) // {value: 3, done: false}
// ...
// workers/counter.js
import { expose } from "threads/worker"

function* startCounting() {
  for (let currentCount = 1; currentCount <= 10; currentCount++) {
    const reset = yield currentCount
    if (reset) {
      currentCount = 0
    }
  }
}

expose(startCounting)

kimamula avatar May 28 '20 04:05 kimamula