Home > C#, javascript, yield return > JavaScript Generators

JavaScript Generators

These work annoyingly well. Why annoying? Because they’re Mozilla specific. If only we could use them all the time. The other thing that’s annoying is how they are actually better than the C# equivalent.

There are two major advantages compared with C#’s yield return.

1. You can use yield inside an anonymous function.

2. You can make yield return a value. No, I don’t mean send a value out of the generator function to the caller. I mean the other way: the caller can send values into the generator function.

Okay, regarding point 2, in my previous ridiculous post I was doing exactly that in C#. But it was awkward:

int i = null;
yield return input => i = input;

I had to yield a lambda so the client could call it, passing me the input, which I then assign to a named variable. Ugh. But in JavaScript in Mozilla, you can do this:

document.body.innerHTML = yield ASYNC.ajax("message.txt");

Note the yield, hiding there in front of an expression. The ASYNC object is a little utility I’ve just written to learn how to use this stuff:

    var ASYNC = {

        ajax: function(url) {

            return function(receiver) {

                jQuery.ajax({ url: url, success: receiver });
            };
        },

        run: function(gen) {

           var quitRequested = false;
           var result = undefined;

           var manager = {

               quit: function() {

                   quitRequested = true;
               },

               step: function() {

                   var y = undefined;

                   try
                   {
                       y = gen.send(result);
                       result = undefined;
                   }
                   catch (e) { }

                   if (quitRequested || y === undefined) {

                       gen.close();

                       if (manager.finished)
                           manager.finished();

                       return;
                   }

                   if (y === null) {

                       window.setTimeout(manager.step, 0);
                       return;
                   }

                   y(function(newResult) {

                       result = newResult;

                       manager.step();
                   });
               }
           };

           gen = gen(manager);

           manager.step();
           return manager;
        }
    };

With this in place, we can do:


$(document).ready(function() {

        ASYNC.run(function() {

            for (var i = 0; i < 100; i++) {

                document.title = i;
                yield null;
            }

            document.body.innerHTML = yield ASYNC.ajax("message.txt");

            for (var i = 0; i < 100; i++) {

                document.title = i;
                yield null;
            }
        });
    });

This counts to 99 on the title bar, then grabs a text file from the server and displays it, then counts to 99 again.

By yielding null, we say “let the browser have a turn”, so the UI can update to reflect the changes we’re making. But we can also yield a function that, when called, returns another function that accepts a function to which an output value can be passed. (Read that a few dozen more times until it goes in.) The ASYNC.ajax function is a fine example. The output value is then stashed away so it can be sent back into the generator and get returned from the next call to yield.

Of course, the advantage of this is that you can write a mix of UI updating and AJAX requests in a linear fashion, instead of having to chain together a zillion callbacks.

I really wish this was in C#… also apparently you can give the generator an exception you’d like it to throw on the next yield, which is beautiful. That way if the AJAX request fails, the yield can throw an error. And that’s something else I’ve previously wished for in C#.

Presumably all this was in CLU’s original version of this idea, but I don’t know anything about that.

Update: on reading further I see the Mozilla team calling this Python-style generators, so it’s all in Python too. And with a bit more searching I see it’s in Ruby. But even better, the thing I was excited about is the potential for using this to implement cooperative coroutines. Ruby has special support for those anyway, which appear to work much more automatically, so I’m going to look at that next.

Before I abandoned this, I couldn’t resist implementing a more complete form of coroutines in Mozilla. Basically I keep a stack of generators, so within one generator you can start another and yield it. But you have to remember to prefix almost every call you make with yield, so it’s hardly seamless.

Advertisements
Categories: C#, javascript, yield return Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: