Search code examples
javascriptasynchronousiterator

How to sequentially chain two sync iterators in JavaScript?


Let's say we have two async iterators,

const asyncIterable1 = {
  [Symbol.asyncIterator]() {
    return {
      i: 0,
      next() {
      if (this.i < 3) {
        return Promise.resolve({ value: this.i++, done: false });
      }

      return Promise.resolve({ done: true });
    }
  };
 }
};

const asyncIterable2 = {
  [Symbol.asyncIterator]() {
    return {
      i: 3,
      next() {
      if (this.i < 5) {
        return Promise.resolve({ value: this.i++, done: false });
      }

      return Promise.resolve({ done: true });
    }
  };
 }
};

Now, is there a way to combine these two iterators into one iterator that would return a sequence of 0,1,2 and then 3,4?


Solution

  • Yeah, I'd use yield* for that:

        const combine = (a, b) => (function* () { yield* a; yield* b; })();
        
        const iterator = combine(
          (function* () { yield 1; yield 2; })(),
          (function* () { yield 3; yield 4; })()
        );
        
        console.log(iterator.next(), iterator.next(), iterator.next(), iterator.next(), iterator.next());

    This works analogously for async iterators. You'll loose the return value ("the done yield") of the first iterator though. You could capture it however (the value yield* evaluates to).

    For sure if you're among the people that like to reinvent wheels, you can also implement such functionality "by hand" without generator functions:

    function combine(...iterators) {
      let pos = 0, iterator = iterators[pos];
      return {
        next() {
          let result = { done: true };
          do {
            result = iterator.next();
            if(!result.done) break;
            iterator = iterators[ pos++ ];
          } while(iterator)
              
          return result;
        }
      };
    }
    
     const iterator = combine(
              (function* () { yield 1; yield 2; })(),
              (function* () { yield 3; yield 4; })()
     );
            
     console.log(iterator.next(), iterator.next(), iterator.next(), iterator.next(), iterator.next());