How do I use await inside a generator?

Viewed 4739

I'm having a generator that, among other operations, queries a database, like

function* current(db) {
  const items = await db.collection('...').find({ ... });

  for (const item of items)
    if (...) yield item;
}

which isn't valid syntax. Using promises and yielding from a then, isn't possible either.

What should I do then? How can I use asynchronous operation inside a generator?

2 Answers

Calling await in generators (aka async generators) has been supported natively in Node v10+ (released April 2018) and Chrome since v63, as well as Firefox v57.

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function getAsyncData() {
  await sleep(1000);  // simulate database/network delay...
  return [1, 2, 3, 4, 5];  // ...then return some data
}

// async generator
async function* filterAsyncData() {
  const items = await getAsyncData();  // await call in generator
  
  for (const item of items)
    if (item % 2) yield item;
}

(async function main() {
  // for-await syntax
  for await (const filteredItem of filterAsyncData())
    console.log(filteredItem);
  
  // classic iterator syntax
  const fadIt = filterAsyncData();
  while (true) {
    const result = await fadIt.next();  // note await
    if (result.done) break;
    console.log(result.value);
  }
}());

Compared to a regular (non-async) generators, remember to use await before calling .next(). Alternatively, the new for-await-of syntax has been available since Node v9.2, and can be used in Node v10 without any flags.

For older versions of node (or for Jest), you can use the Babel plugin transform-async-generator-functions.

Related