# 09. Async Generators

Async generators a mixture of async functions and generators. Normal (synchronous) generators help with implementing synchronous iterables. Asynchronous generators do the same for asynchronous iterables. Async generators allow you to create async iterator factories.

* An async generator returns a generator object which follows iterator specification.
* Each invocation `genObj.next()` returns a Promise for an object `{value, done}` that wraps a yielded value.
* Generator doesn't have a natural end - you can't check the end directly and should await for promise to be resolved to get `done`

```javascript
// Note the * after "function"
async function* asyncRandomNumbers() {
  // This is a web service that returns a random number
  const url =
    "https://www.random.org/decimal-fractions/?num=1&dec=10&col=1&format=plain&rnd=new";

  while (true) {
    const response = await fetch(url);
    const text = await response.text();
    yield Number(text);
  }
}

async function example() {
  for await (const number of asyncRandomNumbers()) {
    console.log(number);
    // use break to stop it because generator doesn't have natural end.
    if (number > 0.95) break;
  }
}
```

Like all `for-loops`, you can break whenever you want. This results in the loop calling `iterator.return()`, which causes the generator to act as if there was a return statement after the current (or next) yield.

## Async generator execution:

* Каждый вызов `next()` возвращает Promise.
* Последующий вызов `next()` вернет следующий Promise безотносительно результата предыдущего Promise (нет необходимости ждать, пока предыдущий Promise будет установлен).
* `next()` вызовы являются неблокирующими.
* Узнать, закончился ли генератор, можно только дождавшись когда Promise будет установлен и проверив значение `done`.
* У асинхронного генератора нет понятия "длинны" или "количества доступных элементов".
* Поэтому асинхронные генераторы не поддерживают `spread` оператор.
* `yield x` fulfills the “current” Promise with `{value: x, done: false}`.
* `throw err` rejects the “current” Promise with `err`.
* In normal generators, `next()` can throw exceptions. In async generators, `next()` can reject the Promise it returns.

## `await` in async generators

You can use `await` and `for-await-of` inside async generators. For example:

```javascript
async function* prefixLines(asyncIterable) {
  for await (const line of asyncIterable) {
    yield "> " + line;
  }
}
```

One interesting aspect of combining `await` and `yield` is that `await` can’t stop `yield` from returning a Promise, but it can stop that Promise from being settled.

## Fetch number of elements

Retrieving Promises to be processed via `Promise.all()`. If you know how many elements there are in an async iterable, you don’t need to check `done`.

```javascript
async function* createAsyncIterable(syncIterable) {
  for (const elem of syncIterable) {
    yield elem;
  }
}

const asyncGenObj = createAsyncIterable(["a", "b"]);
const [{ value: v1 }, { value: v2 }] = await Promise.all([
  asyncGenObj.next(),
  asyncGenObj.next()
]);

console.log(v1, v2); // a b
```

## Async data operations

Async generators as sinks for data, where you don’t always need to know when they are done:

```javascript
const writer = openFile("someFile.txt");
writer.next("hello"); // don’t wait
writer.next("world"); // don’t wait
await writer.return(); // wait for file to close
```

## Async generators in depth

a rough approximation of how async generators work:

```javascript
const BUSY = Symbol('BUSY');
const COMPLETED = Symbol('COMPLETED');
function asyncGenerator() {
    const settlers = [];
    let step = 0;
    return {
        [Symbol.asyncIterator]() {
            return this;
        },
        next() {
            return new Promise((resolve, reject) => {
                settlers.push({resolve, reject});
                this._run();
            });
        }
        _run() {
            setTimeout(() => {
                if (step === BUSY || settlers.length === 0) {
                    return;
                }
                const currentSettler = settlers.shift();
                try {
                    switch (step) {
                        case 0:
                            step = BUSY;
                            console.log('Start');
                            doSomethingAsync()
                            .then(result => {
                                currentSettler.resolve({
                                    value: 'Result: '+result,
                                    done: false,
                                });
                                // We are not busy, anymore
                                step = 1;
                                this._run();
                            })
                            .catch(e => currentSettler.reject(e));
                            break;
                        case 1:
                            console.log('Done');
                            currentSettler.resolve({
                                value: undefined,
                                done: true,
                            });
                            step = COMPLETED;
                            this._run();
                            break;
                        case COMPLETED:
                            currentSettler.resolve({
                                value: undefined,
                                done: true,
                            });
                            this._run();
                            break;
                    }
                }
                catch (e) {
                    currentSettler.reject(e);
                }
            }, 0);
        }
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://strctr.gitbook.io/programming/01-languages/javascript/01-language/e-controls/e5-async/09-async-generators.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
