enhance-generator
    TypeScript icon, indicating that this package has built-in type declarations

    3.0.3 • Public • Published

    EnhanceGenerator

    Supported for ES2018 and above

    If you want some middlewares and loop controle methods (sync/async) for GeneratorFunctions(AsyncGeneratorFunctions), then this package is for you.

    Usage

    Lets say we have GeneratorFunction:

    function* genFunc() {
        yield 1
        yield 2
        yield 3
        return 'finish'
    }

    If you want to map yielded values to another ones:

    const { enhance } = require('enhance-generator')
    
    // New GeneratorFunction with additional methods on it.
    const enhanced = enhance(genFunc)
      .map((value, index) => value * 100)
      .forEach((value, index) => {
        console.log(`index: ${index}, value: ${value}`)
      })
    
    
    for(const _ of enhanced());
    // will be printed:
    // index: 0, value: 100
    // index: 1, value: 200
    // index: 2, value: 300
    
    // // Or You can manually call **next**.
    const gen = enhanced()
    while(!gen.next(/* nextArg */).done); // nextArg will be passed to original Generator.

    You can break loop, yield value and continue iteration, or skip both iteration rest and yield stage.

    • continue
      const { enhance } = require('enhance-generator')
      
      const values = []
      for (const val of enhance(genFunc)
          .continue((value, index) => index === 1) // skip rest of middlewares when index === 1   
          .forEach((value, index) => {
              console.log(`index: ${index}`)
          })()
      ) {
        values.push(val)
      }
      // will be printed:
      // index: 0
      // index: 2
      
      console.log(values)
      // will be yielded
      // [ 1, 2, 3 ]
    • break
      const { enhance } = require('enhance-generator')
      
      for (const _ of enhance(genFunc)
          .break((value, index) => index === 1) // break on second forEach(index===1)
          .forEach((value, index) => {
              console.log(`index: ${index}`)
          })()
      );
      // will be printed:
      // index: 0
      
      // will be yielded
      // [ 1 ]
    • skip
      const { enhance } = require('enhance-generator')
      
      for (const _ of enhance(genFunc)
          .skip((value, index) => index === 1) // skip both rest of middlewares and yield stage   
          .forEach((value, index) => {
              console.log(`index: ${index}`)
          })()
      );
      // will be printed:
      // index: 0
      // index: 2
      
      // will be yielded
      // [ 1, 3 ]

    You can provide context - this both to your GeneratorFunction and to middlewares you use

    const { enhance } = require('enhance-generator')
    
    const context = {
        count: 0
    }
    const enhanced = enhance(genFunc)
        .map((value, index) => value * 100)
        .forEach(function (value, index) {
            this.count++
        })
    
    for (const _ of enhanced
        .useThis(() => context)()
    );
    
    console.log(context)
    // will be printed:
    // { count: 3 }

    Only the last useThis of the chain will be called and it will be called at the very beginning of the loop - after GeneratorFunction() call and before first next() call.

    All this methods are immutable, i.e. each call of this methods (map, forEach...) will return a new (Async)GeneratorFunction with additional middleware layer.

    You can also bind context - this to layer, by providing it as second argument to enhance methods, in this case calls to useThis will not be able to change context of that particular layer.

    const { enhance } = require('enhance-generator')
    
    const bindedContext = {
        count: 0
    }
    const enhanced = enhance(genFunc)
        .map((value, index) => value * 100)
        .forEach(function (value, index) {
            this.count++
        }, bindedContext)
    
    const context = {
        count: 0
    }
    for (const _ of enhanced
        .useThis(() => context)() // useThis will not be able to change binded contexts
    );
    
    console.log(bindedContext)
    // will be printed:
    // { count: 3 }
    console.log(context)
    // will be printed:
    // { count: 0 }

    If Your original GeneratorFunction needs context - this, it will be passed through the enhanced one to it. So the behaviour will be the same for each. e.g.

    const { enhance } = require('enhance-generator')
    
    function* genFunc() {
        yield 1 * this
        yield 2 * this
        yield 3 * this
        return 'finish'
    }
    const context = 100
    
    for (const _ of enhance(genFunc)
        .forEach(function (value, index) {
            console.log(value)
        }).call(context)
    );
    // will be printed
    // 100
    // 200
    // 300

    You can also automate next(arg) call`s argument calculations

    const { enhance } = require('enhance-generator')
    
    function* genFunc() {
        let nextArg = yield 'enter loop'
        nextArg = yield nextArg
        nextArg = yield nextArg
        return 'exit loop'
    }
    
    let nextArg = 0
    for (const _ of enhance(genFunc)
        .useNext(() => ++nextArg)
        .forEach(function (value, index) {
            console.log(value)
        })()
    );
    // will be printed
    // 'enter loop'
    // 1
    // 2

    useNext layers will be called before each iteration except first one, in the same order they occure in the chain, before all other layers. The returned value of the last one will be used to call next(arg) on the Origin Generator.

    Example with multiple useNext layers

    const { enhance } = require('enhance-generator')
    
    function* genFunc() {
        let nextArg = yield 'enter loop'
        nextArg = yield nextArg
        nextArg = yield nextArg
        return 'exit loop'
    }
    
    let nextArg = 0
    for (const _ of enhance(genFunc)
        .useNext(() => ++nextArg)
        .useNext(() => ++nextArg)
        .forEach(function (value, index) {
            console.log(value)
        })
        .useNext(() => ++nextArg)()
    );
    // will be printed
    // 'enter loop'
    // 3
    // 6

    NOTE: The first call next(arg) just enter the loop, it`s argument thrown by Generator itself, so the first iteration is skiped.

    EnhancedGeneratorFunctions are simple GeneratorFunctions Or AsyncGeneratorFunctions with some additional methods on them, So you can pass them to any library that accepts GeneratorFunctions Or AsyncGeneratorFunctions

    Async

    You can Use async functions, in layer addition methods (map,forEach...), also you can use AsyncGeneratorFunctions when calling enhance(generatorFunction). Saying async, we mean explicit usage of async keyword. From the point of the time that you used AsyncFunction or AsyncGeneratorFunction, resulting EnhancedGeneratorFunctions will be async as well. You can check if it is with property isAsync on returned EnhancedGeneratorFunctions.

    NOTE: In this cases you must be careful and be sure you call them with for await of... and not with for of....

    When you instead of async keyword use a simple function which returns Promise it will not be awaited, it will be passed directly (if it expected to be passed, as with map) to next layer as simple value.

    Install

    npm i enhance-generator

    DownloadsWeekly Downloads

    12

    Version

    3.0.3

    License

    ISC

    Unpacked Size

    105 kB

    Total Files

    14

    Last publish

    Collaborators

    • avatar