Skip to content

Instantly share code, notes, and snippets.

@Adriem
Last active April 29, 2020 12:36
Show Gist options
  • Select an option

  • Save Adriem/8683a8dd5aa2ee3bf460a09e30835d57 to your computer and use it in GitHub Desktop.

Select an option

Save Adriem/8683a8dd5aa2ee3bf460a09e30835d57 to your computer and use it in GitHub Desktop.

Revisions

  1. Adriem revised this gist Apr 29, 2020. 1 changed file with 41 additions and 36 deletions.
    77 changes: 41 additions & 36 deletions decorate-generator.js
    Original file line number Diff line number Diff line change
    @@ -5,64 +5,69 @@
    // 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));
    functions.reverse().reduce((decorated, decorator) => decorator(decorated));

    const decorateGeneratorFunction = (...decorators) => (generatorFunction) => {
    let resolve;
    let promise = new Promise(r => resolve = r);
    let resolve;
    let promise = new Promise(r => resolve = r);
    let isGeneratorDone = false;

    const onYield = (yieldedResult) => {
    resolve(yieldedResult);
    promise = new Promise(r => resolve = r)
    };
    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 wrappedGenerator = async (...args) => {
    const generator = generatorFunction(...args);
    for await (let step of generator) onYield(step);
    isGeneratorDone = true;
    };

    const composed = compose(...decorators)(wrappedGenerator);
    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;
    };
    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
    // 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');
    };
    // 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);
    };
    // 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 INITIAL_VALUE = 10;
    const ITERATIONS = 5;

    const decoratedGeneratorFn = decorateGeneratorFunction(
    const decoratedGeneratorFn = decorateGeneratorFunction(
    decorateWithInitialValue(INITIAL_VALUE),
    )(originalGeneratorFn);

    const generator = decoratedGeneratorFn(ITERATIONS);

    let result;
    for await (result of generator) console.log(result);
    return result
    for await (result of generator) console.log(result);
    return result
    };

    main = async () => {
    await demo();
    console.log('Finished');
    };

    demo();
    main();
  2. Adriem created this gist Apr 29, 2020.
    68 changes: 68 additions & 0 deletions decorate-generator.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    // 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
    };

    demo();