W3C home > Mailing lists > Public > public-script-coord@w3.org > October to December 2011

Re: [WebIDL] Simplify callbacks

From: Ojan Vafai <ojan@chromium.org>
Date: Thu, 15 Dec 2011 13:11:23 -0800
Message-ID: <CANMdWTvdDvw=_gmnrL6pXubNb0rRW2_M5PRzq1DXow9Mgh-CFQ@mail.gmail.com>
To: Jonas Sicking <jonas@sicking.cc>
Cc: Ian Hickson <ian@hixie.ch>, Alex Russell <slightlyoff@google.com>, Cameron McCormack <cam@mcc.id.au>, "public-script-coord@w3.org" <public-script-coord@w3.org>, Anne van Kesteren <annevk@opera.com>
On Thu, Dec 15, 2011 at 12:49 PM, Jonas Sicking <jonas@sicking.cc> wrote:

> On Thu, Dec 15, 2011 at 11:47 AM, Ojan Vafai <ojan@chromium.org> wrote:
> > On Wed, Dec 14, 2011 at 9:15 PM, Jonas Sicking <jonas@sicking.cc> wrote:
> >>
> >> On Wed, Dec 14, 2011 at 4:35 PM, Ian Hickson <ian@hixie.ch> wrote:
> >> > On Wed, 14 Dec 2011, Cameron McCormack wrote:
> >> >>
> >> >> Either we embrace object-with-property for all APIs that take
> >> >> callbacks,
> >> >> giving them meaningful method names, or we decide that all future
> APIs
> >> >> take only Functions.  Allowing object-with-property for new APIs but
> >> >> using handleEvent for them all seems like a sucky compromise to me.
> >> >
> >> > I think that the sucky situation would be to have lots of places that
> >> > take
> >> > callbacks, but have to keep looking up what the heck the function is
> >> > called in order to use the object form.
> >> >
> >> > Consider an API that you can use whenever a callback is needed to
> track
> >> > the number of calls to the callback. If every callback uses a
> different
> >> > function name, you'd have to tell this API what the context of the
> >> > callback was instead of just being able to use it blindly. Consider
> what
> >> > it would take to change the implementation of such an API from using
> >> > closures to using an object to store data. Suddenly, every call site
> >> > would
> >> > need to be updated to provide the callback method name.
> >> >
> >> > Don't think of "handleEvent" as meaning "handle a DOM Event object".
> >> > Think
> >> > of it as "handle an event", the event being "the callback was
> invoked".
> >> > There's plenty of precedent for callbacks being called "HandleEvent".
> >>
> >> We have at least 3 options here:
> >>
> >> 1. Accept only Functions (except where webcompatibility requires
> >> otherwise)
> >> 2. Accept Functions and objects with a handleEvent function
> >> 3. Accept Functions and objects with a descriptive function name.
> >>
> >> If the callback has the same name everywhere then it adds absolutely
> >> no value over simply accepting only functions. People would end up
> >> having to write code like
> >>
> >> x = {
> >>  handleEvent: function(args) {
> >>    if (... detect first callback type based on args ...) {
> >>      doStuff();
> >>    }
> >>    if (... detect second callback type based on args ...) {
> >>      doOtherStuff();
> >>    }
> >>    etc;
> >>  },
> >>  doStuff: function() {...},
> >>  doOtherStuff: function() {...},
> >>  moreProps: "here"
> >> }
> >>
> >> registerCallbackFunc(x);
> >> registerOtherCallbackFunc(x);
> >>
> >>
> >> Compare this to:
> >>
> >> x = {
> >>  doStuff: function() {...},
> >>  doOtherStuff: function() {...},
> >>  moreProps: "here"
> >> }
> >>
> >> registerCallbackFunc(function() { x.doStuff() });
> >> registerOtherCallbackFunc(function() { x.doOtherStuff() });
> >>
> >> The latter is much cleaner and understandable. Not to mention that the
> >> first pattern doesn't work at all if you can't tell the types of
> >> callbacks apart based on what arguments they are given.
> >>
> >> So for the sake of not introducing useless features, I would say that
> >> 1 is strictly better than 2. That way people can use Function.bind and
> >> direct callbacks directly to where they want.
> >>
> >>
> >> However I think 3 has value in that it allows you to create a single
> >> object which listens to callbacks from many different APIs.
> >>
> >> x = {
> >>  onCallbackFunc: function() {...},
> >>  onOtherCallbackFunc: function() {...},
> >>  moreProps: "here"
> >> };
> >>
> >> registerCallbackFunc(x);
> >> registerOtherCallbackFunc(x);
> >>
> >>
> >> I don't think that having to memorize the callback names will be any
> >> worse having to remember any other names in the DOM. I think the DOM
> >> would be an even worse API if we renamed all functions to "function"
> >> and all the properties to "property".
> >
> >
> > I don't see the value in option 3. I have yet to see an example where
> option
> > 3 gives you something meaningfully better than just passing a function.
> It's
> > complexity without significant benefits.
>
> The UndoManager is a great example of where this is done in a
> meaningfully better way than can be done with functions.
>

That's not the same thing. The UndoManager never takes a callback. It
explicitly only takes an object and it makes sense in that case since it
needs to support multiple callbacks. The equivalent would be an API that
takes multiple callbacks.

> My preference would be to do option 1, but I'd also be OK with option 2
> > because we want to be consistent across the platform and we can't kill
> > handleEvent on the old APIs.
>
> I assume that you say that we can't kill the handleEvent API because
> people are using it? If so, then clearly someone did think that
> handleEvent was better than simply using functions.
>

That's a non-argument. Someone also thought making attributes full-fledged
Nodes was a good idea. Someone thought inline event handlers having the
element on the scope chain was a good idea. Just because some sites use it
doesn't mean they were right.

It would be good if we had data on the number of sites that use this. I'd
expect the number to be tiny.

 > At some level, option 2 is more simple because
> > it means all these APIs can be handled with the same code in
> > implementations.
>
> That is unlikely to be true given that various API pass different
> arguments to the callback.
>
> / Jonas
>
Received on Friday, 16 December 2011 02:47:53 UTC

This archive was generated by hypermail 2.3.1 : Wednesday, 8 May 2013 19:30:05 UTC