W3C home > Mailing lists > Public > www-dom@w3.org > January to March 2013

Re: Better event listeners

From: Jonas Sicking <jonas@sicking.cc>
Date: Thu, 14 Mar 2013 00:12:10 -0700
Message-ID: <CA+c2ei_WkwvtQNyqSRc6MreM49YH42d-AF-OQduXvtwZfyRzhQ@mail.gmail.com>
To: Bjoern Hoehrmann <derhoermi@gmx.net>
Cc: Anne van Kesteren <annevk@annevk.nl>, Yehuda Katz <wycats@gmail.com>, www-dom@w3.org, slightlyoff@google.com
On Mar 13, 2013 10:04 AM, "Bjoern Hoehrmann" <derhoermi@gmx.net> wrote:
>
> * Jonas Sicking wrote:
> >The exact syntax here is of course to-be-determined, but the idea is
> >that it's the exact same syntax as the "on" function, except that it
> >doesn't take a handler-function argument. Instead a Future is returned
> >and this future is resolved (using the Event object) the first time
> >the corresponding "on" handler-function would have been called.
> >
> >One tricky issue is the exact timing of the call to the .then
> >function. It would be great if we could enable the resolver function
> >to do things like call .preventDefault on the event, but that might
> >require that the .then function is called synchronously which goes
> >against [1].
> >
> >[1] https://github.com/slightlyoff/DOMFuture
>
> Could you elaborate on where you see the problem here?

Currently [1] defines that a future is always resolved at the end of
the current microtask. So when you call resolver.accept it won't
synchronously call the callbacks registered through .then(). Nor is
the .state or .value changes synchronously.

Instead these things happen at the end of the microtask.

This works great for most events. For a "click" event fired in
response to the user clicking a link, we'd call resolver.accept to
resolve the Future. Since we're not currently in a microtask, that
means that we would immediately update the Future's state and start
calling the .then()-registered callbacks.

Another way to look at it is that the implementation of .once() would
look something like:

EventTarget.prototype.once = function(eventType, options) {
  var self = this;
  return new Future(function (resolver) {
    self.on(eventType, options, function(event)) {
      resolver.accept(event);
    });
  });
};

Here the returned Future would be resolved at the end of the
microtask, which means as soon as the event handler exits, but before
the next event handler runs. I.e. the Future would be resolved at the
same time as a normal event handler would be run. I.e. things work
great.

However, if the event is dispatched synchronously, say for example the
loadstart event dispatched in response to a call to XHR.send(), or a
custom event dispatched manually using EventTarget.dispatchEvent(),
things don't work as well.

In this case the entire event dispatch, and calling all event
handlers, happens as part of a single microtask. So "the end of the
microtask" isn't reached until after all of the event dispatched has
happened.

We could solve this by defining that the EventTarget code calls some
internal hidden function on the resolver which synchronously resolves
the Future. But that doesn't seem like a good solution.

Ideally I think we'd never fire events synchronously, but rather at
the end of microtasks or asynchronously, in which case this wouldn't
be a problem. But that ship sailed many years ago.

I suspect this is something that needs to be fixed in the Futures API
and not in the event dispatch code.

/ Jonas
Received on Thursday, 14 March 2013 07:13:10 GMT

This archive was generated by hypermail 2.3.1 : Thursday, 14 March 2013 07:13:15 GMT