Skip to content

Generator functions work flow

Maurizio Lupo edited this page Sep 15, 2018 · 1 revision

A generator function (both sync and async) is composed by an initialisation, a body and a clean up section. Here's an example:

async function * mygen(query) {
  try {
    const db = opendb() // init
    const cursor = db(query) // body
    for await (const record of cursor) {
      yield record
    }
  } finally {
    db.close() // clean up
  }
}

The first iteration will execute both initialisation and body, and return the first record. The next iterations will consume the cursor (in the example) until it reaches the clean up section. Note that the consumer might decide to not finish consuming the generator, this is the reason why it is important to wrap the clean up section in a finally block. The finally block is executed when the method "return" of a generator is called.

Now consider that you are consuming a generator object with a for .. of loop:

for await (const record of mygen(query)) {
  ...
}

Any attempt to exit the for loop prematurely (break, return, throw etc.) http://exploringjs.com/es6/ch_iteration.html#_closing-iterators-via-return will result in the call of the method "return" or the generator.

But if you are consuming the generator object manually, you shouldn't forget to ensure that the "return" method is called eventually (wrapping it in finally is a good idea).

const gen = mygen(query)
try {
  while (true) {
    const next = await gen.next()
    if (next.done) break
    // do something with next.value
  }
  finally {
    if (typeof gen.return === 'function') gen.return()
  }
}

The return method could not be implemented so better check before calling it. This may happen if you are consuming an object implementing the iterable api, that was not derived from a generator function.