Skip to content

Instantly share code, notes, and snippets.

@gaswelder
Created June 23, 2019 23:56
Show Gist options
  • Save gaswelder/d9a520c9a846daad8705046f7da242b4 to your computer and use it in GitHub Desktop.
Save gaswelder/d9a520c9a846daad8705046f7da242b4 to your computer and use it in GitHub Desktop.
Coroutines emulation in Javascript
// This is an example of how cooperative multitasking could be
// emulated in Javascript.
// Javascript has only generators, which are "half-coroutines",
// meaning they can only yield to their called and not other
// generators. We can add a special "coordinator" generator and
// a yielding convention to make it look almost exactly like
// we have full coroutines.
// Note that the yield* operator is not what we need here.
// The yield* operator creates a new instance of a generator
// instead of jumping to an existing generator.
const queue = [];
main();
/**
* main simply creates a coordinating routine and runs it.
*/
function main() {
for (const _ of cooperate(producer, consumer));
}
/**
* cooperate serves as a coordinating generator for other generators,
* allowing them to behave like coroutines.
*
* The list of the generators must be known upfront.
* Every such "cogenerator" has to yield the pointer of the next generator
* to be called.
*
* @param {...any} generators List of generator functions
*/
function* cooperate(...generators) {
// A generator -> iterator map.
const procs = new Map(generators.map(g => [g, g()]));
// Start with "any" routine.
let next = generators[0];
while (true) {
// Call the routine. The routine will do its work
// and yield a pointer to the generator function
// the routine of which we should call next.
const e = procs.get(next).next();
// Assume e.done is always false.
if (e.done) {
throw new Error("hmmm");
}
next = e.value;
// Before jumping to the next routine yield to the function
// that called this scheduler so that it could do some work
// of its own.
yield;
}
}
function* producer() {
while (true) {
while (queue.length < 10) {
const x = Math.random();
queue.push(x);
console.log("produced", x);
}
yield consumer;
}
}
function* consumer() {
while (true) {
while (queue.length > 0) {
const x = queue.shift();
console.log("consumed", x);
}
yield producer;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment