Waiting for DOM Events using `yield`, better-world version

So,

  I very strongly prefer writing code that always works, and code that
does not look like it always works correctly is hard to swallow. Doubts
and bugs tend to go hand in hand. There are some implications with that,
I wouldn't be very happy to write `querySelector('linearGradient')` if
that matches non-SVG `<linearGradient>` elements, and when I see, say,

    my $query = "SELECT ObjectId" .
              "  FROM Transactions JOIN Attachments" .
              "    ON Attachments.TransactionId = Transactions.id" .
              " WHERE ObjectType = 'RT::Ticket'" .
              "   AND MessageId = '" . $MessageId . "';";

as via <http://www.w3.org/blog/systeam/2009/05/19/tracking_requests/>
then <https://lists.w3.org/Archives/Team/sysreq/2009May/0227.html> I'd
report the bug. In <news:3a270eaf.15148572@news.bjoern.hoehrmann.de>,
and I would link the Google Groups version here, but the "new" Google
Groups ... one thing that bugs me about it is that someone at Google
used the wrong GWT options to make it, for me it loads a 2.5 MB script
that, for instance, has 1493 functions, in a string constant, like

  function ABC(a){this.a=a}

Anyway, that message is from the year 2000, I was still in school and
a year before I didn't even have an e-mail address, but I was following
the german PHP and Perl newsgroups and people were building lots of web
sites with user account management, and I learned "hashing passwords" is
a thing. This whole "one-way-function" business made no sense to me, as
I encountered it in that context anyway, and being very curious, I ended
up experimenting with them.

Before Google Groups we had DejaNews, which had the nice slogan "Share
what you know, learn what you don't", and in that message I shared, in
the german PHP newsgroup at the time, a Perl script (poor form, I know)
that showed what I learned, namely that hashing passwords is no better
than storing them literally for practical purposes, by computing all the
hashed passwords up to a certain length and comparing it to a given one,
noting that for a couple of thousand Deutsche Mark you could persist all
the hashed passwords to disk and just look them up (now known as "rain-
bow tables").

Actually, the way I was looking at this at the time was more that I did
not want anybody to know my passwords! Very much including the sites I
might sign up with. That seemed to be the whole idea, only I and nobody
else should know about it. Especially because I might want to re-use my
passwords, but when I sign up with the wrong service, they might be able
to compromise my other accounts! This whole thing didn't make a lot of
sense to me, and still doesn't, not that I know why I should have about
a zillion of Bugzilla and mailman accounts across the web to begin with,
yet, in the mainstream news over the past few years there have been lots
of cases with "hacked web sites" where password were, at best, "hashed".

With no salt, I will add. With my Usenet posting above, it was followed
up with by Kristian Koehntopp, best known to me as managing the german
PHP community very well, maintaining an elaborate FAQ about it at the
time and encouraging good behavior in the PHP newsgroups, from which I
learned a lot, ... pointing out that the problem I mentioned is why it's
common to add salts to the password before hashing and storing it. I did
not know anything about salts back then, just as I don't know why people
do not know about salts today...

Anyway, we want to wait for DOM Events using `yield` which brings me to
http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/0333.html
http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/0337.html
and then finally back to my original Waiting for DOM Events using yield
at <http://lists.w3.org/Archives/Public/www-archive/2008Jul/0009.html>.

I made http://www.websitedev.de/temp/purple-clouds-canvas.html and as is
usual with web browsers, this worked fine in some, and partially in some
and not at all in others, even though they all supposedly supported the
features it uses. It basically just loads two PNGs, scales them using a
`<canvas>`, and then uses the bigger versions in a background image ani-
mation. The images are partially transparent, and they move at different
speeds, so you get an illusion of multi-layer movement.

Now, as it turned out, you can draw images onto a canvas that have not
yet fully loaded without some obvious signal that you are doing anything
wrong, at least not at the time. This was round about the first time I
used the damn thing, so I asked around and debugged a bit, identified
that as the cause, and eventually came up with another version that does
wait http://www.websitedev.de/temp/purple-clouds-canvas-2.html for the
two images to load.

I had to write a state machine that, in this version, counts the images
that have successfully loaded, with code that requires updates in more
than one place if I later decided to load three images instead of two,
which would obviously lead to more bugs, and if I wanted to avoid that,
I would have to write some "load multiple images" library making even
more of a mess. Browser vendors had, at that point, long ago decided
that there should be "no more synchronous APIs", but for plumbing like
what was needed here they had nothing...

So, in that message above I point out that, essentially, generators as
they had been implemented in Firefox by then encapsulate continuations,
so there is no need to manually write continuation-passing-style code;
in the version there I ended up with

  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 ...

Besides the obvious problem that you actually want to load the various
images in parallel, as the browser sees fit, which would require some-
thing akin to `WaitForMultipleObjects` as it is called in the Win32 API,
I actually started out with

  yield WaitForEvent(...);

But that requires setting `src` before registering the listeners, and
back then that would lead to bugs, and five years later it still does.
I liked http://lists.w3.org/Archives/Public/www-tag/2013Mar/0024.html
where Alex Russell notes the problem of code that looks like it has ti-
ming issues "but doesn't" (actually "does", due to bugs). So I went with
the code above, where there is no chance that the listener has not been
registered before the load event is dispatched.

I also didn't want to learn all about the "interesting" practise of
throwing a `StopIteration` iteration exception to signal that there are
no more elements to be expected from a generator, and being unable to
obtain a reference to the generator object from the generator code was
also frustrating (can you write a Y-combinator for this case?) and as
handling multiple images would have required a lot more code...

Well I figured just get the main point across, and I think it did that.
In the years after, there did not really seem to be any interest to make
any headway in this direction; on the contrary, as usual, people went
for the local optima, the easy, quick solutions that work right now and
do not require coordination or long-term thinking; rather, there we got
plenty of suggestions to add "sync" APIs to "workers" because "async" is
"sux" but workers don't need "async", right.

But now Futures and Promises are all the rage and you get to see `await`
on es-discuss, and discussions about monad flattening on "GitHub", and
http://lists.w3.org/Archives/Public/www-dom/2013JanMar/0203.html has not
really been resolved yet, as far as I am concerned, so I figured it may
be useful to have a better-world version of the code posted back then:

  function OnYieldWaitForEvent(target, name, useCapture) {
    return function(resume) {
      var listener;
      listener = function(){
        target.removeEventListener(name, listener, useCapture);
        resume();
      };
      target.addEventListener(name, listener, useCapture);
    }
  }
  
  function ExecWithOnYield(func) {
    var generator = new func;
    var ExecOneStep;
    ExecOneStep = function(){
      try {
        var wrapped = generator.next();
        // TODO: what if `wrapped` throws `StopIteration`?
        wrapped(ExecOneStep);
      }
      catch (e) {
        if (e instanceof StopIteration) {
          console.log("StopIteration");
        } else {
          console.log("other exception");
        }
      }
    }
    ExecOneStep(generator);
  };
  
  function test() {
  
    var img1 = new Image(315, 48);
    img1.src = 'http://www.w3.org/Icons/w3c_main';
    yield OnYieldWaitForEvent(img1, 'load', false);
  
    console.log("img1 has loaded when we get here");
  };
  
  ExecWithOnYield(test);

In http://taskjs.org/ my `ExecWithOnYield` is called `spawn`, and the
`OnYieldWaitForEvent` function would return a "Promise", and then the
`ExecWithOnYield` function would probably call `wrapped.then` instead
of calling the function object, and so on, but the ideas are the same,
with devilish details that have yet to be worked out.

As for my question on www-dom, my intuition is that if it is indeed
"best" to ... well, language seems to get awkward here, let's perhaps
say that if it is indeed best when promises can only be kept asyncly,
making that incompatible with the existing event dispatch mechanism,
then some re-architecting is needed to, in essence, bring the use case,
load multiple images to draw on canvas, that got me here, in line with
"Futures". How to go from `new Image(...)` to a `Future` is one problem.

And, perhaps, to wind back to the beginning a second time, in Perl, the
`...` is a valid expression that throws a "Not Implemented" exception if
executed. That is great, because that way I can hint at error handling
code without actually writing it, and unlike a `// ...` comment, it will
work at runtime. In contrast, in my PNG recompressor `pngwolf` I write
stuff like https://github.com/hoehrmann/pngwolf/blob/master/pngwolf.cxx

  if (fwrite(PNG_MAGIC, 8, 1, out) != 1) {
  }

which is strictly worse, but at least you can see easily that something
is missing. And if you start using `...` to mark stubs, say in case of
<https://gist.github.com/5392308> to mark stubs like

  sub binary_search { ... }

you will soon find yourself wanting syntax to mark sections of code that
"cannot happen", which may indeed be "impossible", but only until some-
one modifies the preceding code, and if you fail to encode "this cannot
happen" in a dynamic language where the "compiler" cannot easily tell
you about, say, missing return values, in certain branches, you will end
up having a bad time debugging the code one day. Hence some of the few
`Carp::confess("Impossible")` in the code, but that doesn't look terrib-
ly right to me... Perhaps three U+2620 as expression?

regards,
-- 
Björn Höhrmann · mailto:bjoern@hoehrmann.de · http://bjoern.hoehrmann.de
Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/ 

Received on Wednesday, 24 April 2013 02:08:47 UTC