Last active
April 29, 2020 12:36
-
-
Save Adriem/8683a8dd5aa2ee3bf460a09e30835d57 to your computer and use it in GitHub Desktop.
Enable decorating an asynchronous generator function in javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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