Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Working version of generator async code sample. Using node from https://github.com/andywingo/node/tree/v8-3.19
tim@touchsmart:~/Code$ nvm use v0.11.2-generators
Now using node v0.11.2-generators
tim@touchsmart:~/Code$ node --harmony testgen.js
<Buffer 76 61 72 20 66 73 20 3d 20 72 65 71 75 69 72 65 28 27 66 73 27 29 3b 0a 66 75 6e 63 74 69 6f 6e 20 72 65 61 64 46 69 6c 65 28 70 61 74 68 2c 20 65 6e 63 ...>
Sleeping for 2000ms...
Done
var fs = require('fs');
function readFile(path, encoding) {
return function (callback) {
fs.readFile(path, encoding, callback);
};
}
function sleep(ms) {
return function (callback) {
setTimeout(callback, ms);
};
}
function run(makeGenerator) {
return function () {
var generator = makeGenerator.apply(this, arguments);
var continuable, sync, value;
next();
function next() {
while (!(continuable = generator.send(value)).done) {
continuable = continuable.value;
sync = undefined;
continuable(callback);
if (sync === undefined) {
sync = false;
break;
}
}
}
function callback(err, val) {
if (err) return generator.throw(err);
value = val;
if (sync === undefined) {
sync = true;
}
else {
next();
}
}
}
}
run(function* () {
console.log(yield readFile(__filename));
console.log("Sleeping for 2000ms...");
yield sleep(2000);
console.log("Done");
})();

clifton commented Jun 6, 2013

👍 clean!

Termina1 commented Jun 8, 2013

I don't really think this is clean. It looks too implicit, also I believe that sync variable is not the most elegant way to solve the problem. Moreover, the problem is that function run take too much responsibility working as a proxy between real async functions and code inside the generator (and you need to wrap all functions in another function). In my opinion, the way jmar777/suspend solves this problem providing the resume function is more explicit and clean.

@Termina1 yes, yes, and yes. just about what i thought when perusing the code.

Owner

creationix commented Jun 11, 2013

@Termina1 what's a better way other than the sync variable to detect if the callback was called before or after the continuable returned? I'm open to cleaner code, I don't like all this boilerplate.

Also reading through suspend's code, it appears he is vulnerable to stack overflows if the callbacks are called before the parent function returns. If I didn't have this safeguard, this would be 1/4 the code.

Owner

creationix commented Jun 11, 2013

@Termina1 I will admit though, that not having to wrap any functions before using them is very nice. Might be enough to convince me.

Owner

creationix commented Jun 11, 2013

@Termina1, @loveencounterflow, I made another based on the syntax of suspend https://gist.github.com/creationix/5761021

txdv commented Jun 28, 2013

TypeError: Object # < GeneratorFunctionPrototype > has no method 'send'

I am using node 0.11.3 installed Q via npm, any advice?

jeswin commented Aug 4, 2013

@txdv send is now removed from the spec. Use next(value).
https://code.google.com/p/v8/issues/detail?id=2715

@creationix Could you explain the use of sync to detect if the callback was called before or after the continuable returned? I thought the callback will be in a later tick and sync will always be undefined?

Yes, can you explain the logic of sync? It does not seem consistent.
Secondly, Dao007forever is correct, the callback would not be called until a later tick, so sync is not needed anyway.

callback also can be synchronous,just like
function foo(callback) {
return callback();
}

  1. The fact if the sync is true of false is never used. So, we don't need undefined.
  2. undefined can actually be defined in an outer scope. Always use a local var undefined.
  3. When the callback was not run synchronously and we've breaked from the loop, we call a next recursively. That's not a very good idea, because you can get out of stack if there're too many asynchronous callbacks in each other. I couldn't find a way to counter this issue, though. (How do we use trampolining here at all?) That's what I've been expecting to see in ES6 from the box.

EDIT. From the fact that those variables are defined outside of next I'm guessing that you already know about (3) and you've been trying to save stack space. Haven't you?

sultan99 commented Aug 20, 2016

I think it could be less code, correct me if I do wrong:

function asyn(generator) {
  var iterator = generator()
  var job = iterator.next()
  var next = function() {
    if (typeof(job.value) == "function") {
      job.value(function(error, val) {
        if (error) return console.error(error)
        job = iterator.next(val)
        if (!job.done) next()
      })
    }
  }
  next()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment