Paul Tarjan
Paul Tarjan

Reputation: 50602

Does node.js support yield?

Is there any way to get generators into node.js?

I'm currently faking them with callbacks, but I have to remember to check the response of the callback inside of my generator function which creates a lot of if (callback(arg) === false) return;

I want something like in python:

for p in primes():
  if p > 100: break
  do_something(p)

which I'm doing in node like this:

primes(function(p) {
  if (p > 100) return false;
  do_something(p)
});

Maybe something like coffeescript could help?

Upvotes: 49

Views: 20786

Answers (10)

John
John

Reputation: 7329

Yes Node.js and JavaScript now have both synchronous iterators (as of atleast Node v6) and asynchronous iterators (as of Node v10):

An example generator/iterator with synchronous output:

// semi-pythonic like range
function* range(begin=0, end, step=1) {
  if(typeof end === "undefined") {
    end = begin;
    begin = 0;
  }
  for(let i = begin; i < end; i += step) {
    yield i;
  }
}

for(const number of range(1,30)) {
  console.log(number);
}

A similar async generator/iterator.

const timeout = (ms=1000) => new Promise((resolve, reject) => setTimeout(resolve, ms));

async function* countSeconds(begin=0, end, step=1) {
  if(typeof end === "undefined") {
    end = begin;
    begin = 0;
  }
  for(let i = begin; i < end; i += step) {
    yield i;
    await timeout(1000);
  }
}

(async () => {
  for await (const second of countSeconds(10)) {
    console.log(second);
  }
})();

There is a lot to explore here are some good links. I will probably update this answer with more information later:

Upvotes: 0

DDS
DDS

Reputation: 4375

You can use generators in Node.js, but only in 0.11+. Node.js 0.12 (stable) is now available. Add --harmony_generators or --harmony to the command line parameters of node to enable it.

With Traceur, you can compile advanced JavaScript to vanilla JavaScript. You could make a loader for node.js that does this on-the-fly. Since it runs on, and compiles to vanilla JavaScript, it runs in node.js < 0.11 as well as in the browser.

Facebook has developed a lighter version that only supports generators, called Regenerator. It works similarly to Traceur.

Upvotes: 4

manojlds
manojlds

Reputation: 301087

We are using gnode for generators in node < 0.11.3 - https://github.com/TooTallNate/gnode

Upvotes: 0

Tor Valamo
Tor Valamo

Reputation: 33749

Update 2014: Node does support callbacks now. The following is a post from 2010.


You should use callbacks. If the function does something asynchronously, you may also want a continuation callback (continuation is a bad word, since it also means something else, but you get my point.)

primes(function(p) {
  if (p > 100) return false // i assume this stops the yielding
  do_something(p)
  return true // it's also better to be consistent
}, function(err) { // fire when the yield callback returns false
  if (err) throw err // error from whatever asynch thing you did
  // continue...
})

Updated with example code

I flipped it, so that it returns true on complete (since null, false and undefined all evaluate to false anyways).

function primes(callback) {
  var n = 1, a = true;
  search: while (a)  {
    n += 1;
    for (var i = 2; i <= Math.sqrt(n); i += 1)
      if (n % i == 0)
        continue search;
    if (callback(n)) return
  }
}

primes(function(p) {
  console.log(p)
  if (p > 100) return true
})

Upvotes: 0

Peeter
Peeter

Reputation: 9382

Apparently not in the current stable version. You can however achieve the same using node-fibers + promises.

Here is my implementation:

var fiber = require('fibers');

module.exports.yield = function (promise) {

    var currentFiber = fiber.current;
    promise
        .then(function (value) {
            currentFiber.run(value);
        })
        .otherwise(function (reason) {
            currentFiber.throwInto(reason);
        });

    return fiber.yield();
};
module.exports.spawn = function (makeGenerator) {
    fiber(function () {
        makeGenerator.apply(this, Array.prototype.slice.call(arguments, 1));
    }).run();
};

And a sample code on how it works: (query.find returns a promise)

        var generators = require('./utils/generators');
        var query = require('./utils/query');

        generators.spawn(function () {
            try {
                var field1 = generators.yield(query.find('user', { _id : '1' }));
                var field2 = generators.yield(query.find('user', { _id : '2' }));
                console.log('success', field1[0]._id, field2[0]._id);
            }
            catch (e) {
                console.error('error', e);
            }
        });

Upvotes: 3

disfated
disfated

Reputation: 10999

The issue proposing generatiors in v8 has recently been accepted by v8 project member.
Please vote there to make yield come true.

Upvotes: 2

Not a Name
Not a Name

Reputation: 1003

Yes and no.

var myGen = (function () {
    var i = 0;
    return function () {
        i++; return i; }
})();
var i;
while ((i = myGen()) < 100 ) {
    do something; }

As you see, you can implement something like one using closures, but it does not have native generators.

Upvotes: 2

Paul Tarjan
Paul Tarjan

Reputation: 50602

The answer is "not currently" but Marcel seems to be my hero. Lets hope this goes somewhere:

https://groups.google.com/forum/#!msg/nodejs/BNs3OsDYsYw/oCsWBw9AWC0J https://github.com/laverdet/node-fibers

Upvotes: 8

user471679
user471679

Reputation:

You might check out wu.js at http://fitzgen.github.com/wu.js/ It has lots of interesting iterator functions.

Upvotes: 2

Related Questions