Skip to content

Instantly share code, notes, and snippets.

@Adriem
Last active April 29, 2020 12:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Adriem/8683a8dd5aa2ee3bf460a09e30835d57 to your computer and use it in GitHub Desktop.
Save Adriem/8683a8dd5aa2ee3bf460a09e30835d57 to your computer and use it in GitHub Desktop.
Enable decorating an asynchronous generator function in javascript
// When a generator function is decorated or composed, the composed function will not
// work unless the decorator explicitly supports decorating a generator function.
//
// This example shows how to wrap an async generator function as an async function, pass it
// to the decorators and wrap the result in an async generator synchronized with the original one.
const compose = (...functions) =>
functions.reverse().reduce((decorated, decorator) => decorator(decorated));
const decorateGeneratorFunction = (...decorators) => (generatorFunction) => {
let resolve;
let promise = new Promise(r => resolve = r);
let isGeneratorDone = false;
const onYield = (yieldedResult) => {
resolve(yieldedResult);
promise = new Promise(r => resolve = r)
};
const wrappedGenerator = async (...args) => {
const generator = generatorFunction(...args);
for await (let step of generator) onYield(step);
isGeneratorDone = true;
};
const composed = compose(...decorators, wrappedGenerator);
return async function* (...args) {
// Do not await composed() function. The wrappedGenerator
// will call onYield(), which will advance the progress
composed(...args);
while (!isGeneratorDone) yield await promise;
};
}
// Decorate the generator and execute it, printing each yield result and awaiting for its end
const demo = async () => {
// Promised timeout
const delay = (t, v) => new Promise(r => setTimeout(() => r(v), t));
// Generator function to be passed down to a decorator that expects an async function
const originalGeneratorFn = async function* (initialValue, times) {
for (let i = 1; i <= times; i++) yield await delay(500, initialValue + i);
yield await delay(500, 'DONE');
};
// Decorate an asynchronous function to be called
// with only an argument containing the value <0>
const decorateWithInitialValue = (initialValue) => (fn) => async function (...args) {
return await fn(initialValue, ...args);
};
const INITIAL_VALUE = 10;
const ITERATIONS = 5;
const decoratedGeneratorFn = decorateGeneratorFunction(
decorateWithInitialValue(INITIAL_VALUE),
)(originalGeneratorFn);
const generator = decoratedGeneratorFn(ITERATIONS);
let result;
for await (result of generator) console.log(result);
return result
};
main = async () => {
await demo();
console.log('Finished');
};
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment