W3C home > Mailing lists > Public > www-archive@w3.org > July 2008

Waiting for DOM Events using `yield`

From: Bjoern Hoehrmann <derhoermi@gmx.net>
Date: Fri, 04 Jul 2008 20:56:22 +0200
To: www-archive@w3.org
Message-ID: <ivqs64145iubof59rumnhaf0bp1anqjmsu@hive.bjoern.hoehrmann.de>

Hi,

  Coordinating asynchronous operations like resource loading has so far
been quite difficult in JavaScript as it lacks, among many other things,
synchronization primitives. Drawing three images on a <canvas> can be
quite complicated as you cannot draw the image until it finished loading
and the only way to wait for that is by way of a 'load' event handler.

Writing code for that can be complicated, and one is likely to resort to
pre-loading the images up front to avoid some of the complexity, giving
the user a sub-optimal experience. The `yield` statement proposed for
the next version of ECMAScript and implemented in JavaScript 1.7 can be
used to write the loading code in a slightly more natural fashion, as
demonstrated below.

The code below defines two functions, ExecWithOnYield which takes a
function containing `yield` and executes it. It is there primarily to
work around the lack of an `arguments.generator` property (the usual
`arguments.callee` chain does not work with generator for some reason).
It simply initializes the generator and passes the generator object to
the generator.

The second function, OnYieldWaitForEvent, takes the generator object
and event listener registration arguments, and then ensures that the
function being executed will continue once the event occured. It could
be simplified a bit if the phase parameter is omitted and the function
added as method to the EventTarget interface, but that's also a bit
weirder.

  <script type="application/javascript;version=1.7">

  function OnYieldWaitForEvent(cor, target, name, phase) {
    var self = arguments.callee;
    target.addEventListener(name, function(evt) {

      // We need to unregister the listener to save resources
      // and to avoid getting called if the event occurs again
      evt.currentTarget.removeEventListener(name, self, phase);

      // Resume the waiting function
      cor.next();
    }, phase);
  };

  function ExecWithOnYield(func) {
    var generator = new func;
    generator.next();

    // There is no arguments.generator or `this generator` yet
    // and arguments.callee refers to the Function object, so
    // we have to pass a self-reference to the instance.
    generator.send(generator);
  };

  function test() {
    var self = yield;

    // Create a new <img> object
    var img1 = new Image(315, 48);

    OnYieldWaitForEvent(self, img1, 'load', false);
    img1.src = 'http://www.w3.org/Icons/w3c_main';
    yield; // wait for img1 'load'

    // ... load more images here ...

    // Now draw them all on a <canvas>

    // Signal we are done so the event handler does not have to
    // catch an exception that would otherwise be generated.
    yield 'done';
  };

  ExecWithOnYield(test);

  </script>

regards,
-- 
Björn Höhrmann · mailto:bjoern@hoehrmann.de · http://bjoern.hoehrmann.de
Weinh. Str. 22 · Telefon: +49(0)621/4309674 · http://www.bjoernsworld.de
68309 Mannheim · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/ 
Received on Friday, 4 July 2008 18:57:03 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Wednesday, 7 November 2012 14:18:18 GMT