javascriptasync-awaitecmascript-2018

Concise partial application of ES2018 Async Generator?


I have an async generator function defined, and want to create a second async generator function that is a partial application of the first function. For example, this works:

async function* fn1(a, b) {/* do something */}

async function* fn2() {
  const a1 = fn1(0, 0);
  for await (const value of a1) {
    yield value;
  }
}

My question is: is there a more concise way to define fn2?


Solution

  • fn1 returns the async iterable that fn2 is iterating over, so rather than iterating manually in fn2, you can just yield the iterable created from fn1 and let fn2's consumer handle it:

    async function* fn2() {
      yield* fn1(0, 0);
    }
    

    const delay = ms => new Promise(res => setTimeout(res, ms));
    async function* fn1(a, b) {
      for (let i = 0; i < 3; i++) {
        await delay(1000);
        yield i;
      }
    }
    
    async function* fn2() {
      yield* fn1(0, 0);
    }
    
    (async () => {
      for await (const val of fn2()) {
        console.log('Consumer got', val);
      }
      console.log('Done');
    })();

    // Original version, without yield* (just to illustrate equivalence):
    
    const delay = ms => new Promise(res => setTimeout(res, ms));
    async function* fn1(a, b) {
      for (let i = 0; i < 3; i++) {
        await delay(1000);
        yield i;
      }
    }
    
    async function* fn2() {
      const a1 = fn1(0, 0);
      for await (const value of a1) {
        yield value;
      }
    }
    
    (async () => {
      for await (const val of fn2()) {
        console.log('Consumer got', val);
      }
      console.log('Done');
    })();