6

I have an api thats going to return a cursor for fetching more data. I've mocked it out like this:

function fetch(n) {
  return Promise.resolve({
    results: [n],
    next: next < 10 && n + 1,
  })
}

What I'm trying to do is figure out how I can use async/await along with generators in order to interact with this api.

Here's basically what I've prototyped:

async function* api(url) {
  let result = await fetch(url)
  yield result
  while (result.next) {
    result = await fetch(result.next)
    yield result
  }
}

The idea is that I should be able to create an async generator and yield from that generator in order to iterate through the cursor:

async function main() {
  const gen = api(0)
  const zero = await gen.next()
  console.log(zero.result)
  const one = await gen.next()
  console.log(one.result)
  const rest = await Promise.all([...gen])
  console.log(rest.map(r => r.result))
}

All things considered, I think this is a pretty sweet way of handling paginated data and being able to pull out all of the data with [...gen] is pretty damn cool.

Only problem is, it doesn't work! Apprently you can't use async with function*:

❯❯❯ node --version
v7.0.0
❯❯❯ node --harmony --harmony-async-await async-generator.js
/Users/chetcorcos/code/async-generator.js:11
async function* api(url) {
              ^
SyntaxError: Unexpected token *
    at Object.exports.runInThisContext (vm.js:76:16)
    at Module._compile (module.js:545:28)
    at Object.Module._extensions..js (module.js:582:10)
    at Module.load (module.js:490:32)
    at tryModuleLoad (module.js:449:12)
    at Function.Module._load (module.js:441:3)
    at Module.runMain (module.js:607:10)
    at run (bootstrap_node.js:382:7)
    at startup (bootstrap_node.js:137:9)
    at bootstrap_node.js:497:3

But I really feel like this should be possible. There's a popular library called co that I've been poking around with but I don't think that's what I want.

Any ideas how to get this concept of "async generators" to work?

8
  • 5
    Have a look at the upcoming proposal github.com/tc39/proposal-async-iteration . You can use it with Babel's babel-plugin-transform-async-generator-functions transform Commented Dec 27, 2016 at 19:14
  • Is using async/await part of requirement? Commented Dec 27, 2016 at 19:16
  • @FelixKling that proposal is epic. Thanks! Commented Dec 27, 2016 at 19:28
  • @guest271314 not necessarily part of the requirement -- I've been playing around trying to make it happen without the keywords, but its been a struggle. Commented Dec 27, 2016 at 19:30
  • 1
    you can down-compile with typescript or simply run natively with nodejs9 with the --harmony flag: stackoverflow.com/questions/43694281/… Commented Dec 15, 2017 at 10:42

2 Answers 2

2

You can do this using the Babel plugin transform-async-generator-functions.

The usage is like this:

const g = async i => [ 1, 2, 3 ]
  .map(x => x * 10 ** i);

const f = async function * () {
  for (let i = 0; i < 10; i++) {
    const xs = await g(i);
    for (const x of xs) {
      yield x;
    }
  }
};

const main = async () => {
  for await (const x of f()) {
    console.log(x);
  }
};

main().catch(e => console.error(e));

Here is an example repo showing how to setup your project.

The important part is the .babelrc file:

{
  "presets": [ "env" ], 
  "plugins": [ "transform-async-generator-functions" ]
 }
Sign up to request clarification or add additional context in comments.

Comments

0

You can pass call the generator function as parameter to without using spread element, Promise.all() accepts an iterable as parameter which yield returns. Note, Promise.all() does not resolve or reject the passed Promise objects in sequential order, though does return the resulting array in same order as the elements within passed iterable.

let api = (value) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(value)
    }, Math.floor(Math.random() * 3500))
  })
};

let values = [1, 2, 3];
let results = [];
let gen = function* gen(fn, props) {
  let i = 0; 
  do {
    yield fn(props[i]).then(res => {console.log(res); return res});
    ++i;
  } while (i < props.length);
}

Promise.all(gen(api, values))
.then(data => console.log("complete:", data))
.catch(err => console.log(err));

1 Comment

This looks like it could be a good answer... but the wording is unclear: "You can pass call the generator function as parameter to without using spread element"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.