Skip to content
Please note that GitHub no longer supports Internet Explorer.

We recommend upgrading to the latest Microsoft Edge, Google Chrome, or Firefox.

Learn more

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Playing around with `AsyncIterable`s
#!/usr/bin/env ts-node
type ValueFn = (index?: number) => any | Promise<any>;
const identity = v => v;
async function* range(start: number, end: number, value: ValueFn = identity) {
let index = start;
while (index < end) {
yield await value(index);
index++;
}
}
type AllIterators<TValue> =
| Iterable<TValue>
| IterableIterator<TValue>
| AsyncIterableIterator<TValue>;
function wait(wait: number): Promise<void> {
return new Promise(resolve => setTimeout(() => resolve(), wait));
}
function every(ms: number) {
return async function*<TValue>(
generator: AllIterators<TValue>
): AsyncIterableIterator<TValue> {
for await (let v of generator) {
await wait(ms);
yield v;
}
};
}
function between(start: number, _end: number): Promise<number> {
const end = _end !== Infinity ? _end : Number.MAX_SAFE_INTEGER;
return new Promise((resolve, reject) =>
resolve(Math.floor(start + Math.random() * (end - start + 1)))
);
}
async function start() {
for await (let value of every(100)(
range(0, Infinity, () => between(1, 10))
)) {
console.log("value", value);
}
}
start();
@sebinsua

This comment has been minimized.

Copy link
Owner Author

sebinsua commented Jul 4, 2019

@sebinsua

This comment has been minimized.

Copy link
Owner Author

sebinsua commented Jul 4, 2019

As above, you can create an infinite generator, but it can't be reused once it has completed, and unfortunately it appears to move into the completed [[GeneratorState]] if you do things like break from a loop.

However, if you wrap a generator with a function like tiptoe (see below) then the inner generator will not move into a completed state.

#!/usr/bin/env ts-node

type ValueFn = (index?: number) => any | Promise<any>;

const identity = <TValue>(v: TValue): TValue => v;

async function* range(start: number, end: number, value: ValueFn = identity) {
  let index = start;
  while (index < end) {
    yield await value(index);
    index++;
  }
}

type AllIterators<TValue> =
  | Iterable<TValue>
  | IterableIterator<TValue>
  | AsyncIterableIterator<TValue>;

const isAsyncIterator = <TValue>(
  iterator: any
): iterator is AsyncIterableIterator<TValue> => {
  return Symbol.asyncIterator in iterator;
};

const isIterator = <TValue>(
  iterator: any
): iterator is IterableIterator<TValue> => {
  return Symbol.iterator in iterator;
};

async function* tiptoe<TValue>(iterator: AllIterators<TValue>) {
  while (true) {
    if (isAsyncIterator(iterator)) {
      const { value, done } = await iterator.next();
      if (done) {
        break;
      }
      yield value;
    } else if (isIterator(iterator)) {
      const { value, done } = iterator.next();
      if (done) {
        break;
      }
      yield value;
    } else if (Array.isArray(iterator)) {
      for (let value of iterator) {
        yield value;
      }
      break;
    }
  }
}

async function start() {
  const it = range(1, Infinity);
  for await (let v of tiptoe(it)) {
    console.log("first run, value", v);
    if (v === 50) {
      break;
    }
  }

  for await (let v of tiptoe(it)) {
    console.log("second run, value", v);
    if (v === 100) {
      break;
    }
  }
}

start();
@sebinsua

This comment has been minimized.

Copy link
Owner Author

sebinsua commented Jul 4, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.