Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 generators: transforming callbacks to iterators

I'm experimenting with ES6 generators with the help of babel, and I have trouble understand how (or if!) I can effectively use callback based async function to output an iterator.

Let's say I want be able to write a function that takes a number of urls, asynchronously download them and returns them as soon as they are downloaded. I would like to be able to write something like the following:

let urls = ['http://www.google.com', 'http://www.stackoverflow.com' ];
for ( {url, data} of downloadUrls(urls) ) {
    console.log("Content of url", url, "is");
    console.log(data);
}

How can I implement downloadUrls ? Ideally I would like to be able to write the following:

var downloadUrls = function*(urls) {
    for( let url of urls ) {
        $.ajax(url).done( function(data) {
            yield data;
        });
    }
};

This of course doesn't work, since ``yield'' is being invoked inside a callback and not directly inside the generator. I can find many examples online of people trying the same, they are either not much transparent), require enabling browser/node flags, or use node-specific features/libraries. The library closest to what I need seems to be task.js, but I'm unable to have even the simplest example run on current Chrome.

Is there a way to get the intended behaviour using standard and current features , (With current I mean usable with transpilers like babel, but without the need to enable extra flags on the browser) or do I have to wait for async/await ?

like image 545
Marco Righele Avatar asked Apr 17 '15 12:04

Marco Righele


1 Answers

2019 update

Yielding via callbacks is actually pretty simple. Since you can only call yield directly from the generator function* where it appears (and not from callbacks), you need to yield a Promise instead, which will be resolved from the callback:

async function* fetchUrls(urls) {
  for (const url of urls)
    yield new Promise((resolve, reject) => {
      fetch(url, { mode: 'no-cors' }).then(response => resolve(response.status));
    });
}

(async function main() {
  const urls = ['https://www.ietf.org/rfc/rfc2616.txt', 'https://www.w3.org/TR/PNG/iso_8859-1.txt'];
  // for-await-of syntax
  for await (const status of fetchUrls(urls))
    console.log(status);
}());

If the example doesn't work in the browser (it my return 0 instead of 200 due to Cross Origin Read Blocking), try it live on repl.it.

like image 155
Dan Dascalescu Avatar answered Oct 06 '22 00:10

Dan Dascalescu