W3C home > Mailing lists > Public > public-media-capture@w3.org > October 2014

Re: Our error-handling is broken (Re: Promise slides)

From: Harald Alvestrand <harald@alvestrand.no>
Date: Thu, 02 Oct 2014 11:00:25 +0200
Message-ID: <542D1429.5020609@alvestrand.no>
To: public-media-capture@w3.org
On 10/01/2014 10:05 PM, Jan-Ivar Bruaroey wrote:
> In case it's not obvious from the slides, I'll be arguing tomorrow
> that our error-handling is broken and should not be shipped.
> This excellent post on promises [1] says why better than I can:
>> "There are two very important aspects of synchronous functions:
>>     They return values
>>     They throw exceptions
>> Both of these are essentially about composition. That is, you can
>> feed the return value of one function straight into another, and keep
>> doing this indefinitely. More importantly, if at any point that
>> process fails, one function in the composition chain can throw an
>> exception, which then bypasses all further compositional layers until
>> it comes into the hands of someone who can handle it with a catch."
>> Now, in an asynchronous world, you can no longer return values: they
>> simply aren’t ready in time. Similarly, you can’t throw exceptions,
>> because nobody’s there to catch them. So we descend into the
>> so-called “callback hell,” where composition of return values
>> involves nested callbacks, and composition of errors involves passing
>> them up the chain manually, and oh by the way you’d better never
>> throw an exception or else you’ll need to introduce something crazy
>> like domains.
> I will demonstrate.
> First, here's a fiddle [2] showing how promises handle errors correctly:
>     <div id="log"></div>
>     var div = document.getElementById("log");
>     var log = msg => (div.innerHTML = div.innerHTML + msg + "<br>");
>     new Promise(resolve => resolve())
>     .then(() => log("success1"), () => log("fail1"))
>     .then(() => {
>       log("success2a");
>       barf;
>       log("success2b");
>     }, () => log("fail2"))
>     .then(() => log("success3"), () => log("fail3"))
>     .then(() => log("success4"), () => log("fail4"))
>     .then(() => log("success5"), () => log("fail5"))
>     .catch(() => log("failure"));

Now this is interesting, not because of its relationship to
getUserMedia, but because of what it says about the additional
functionality of promises....

In the callback world, we insist that gUM calls *one* function, *once*.
In the promises world, we say that a promise can call *any number* of
functions - and which ones get called is not going to be obvious.

In particular, it seems that if you make a stack of functions like this,
they will interfere with each other; it would be easy to assume that you
can push a success/failure pair on the stack with the assumption that
either the success or failure in each pair got called; instead, we find
that while the original function succeeded, success3 never got called,
instead fail3 got called with an error that was *not* originating from
the base function.

On the other hand, success4 got called exactly as expected, despite the
fact that something went boom somewhere in the processing. So if you
want to be sure 2 success handlers get called on success, you have to do:

              .then(success1, failure1)
              .then(null, failure-in-handler-set-1)
              .then(success2, failure2)

That's not necessarily a bad thing. But it's not unsubtle either.
Received on Thursday, 2 October 2014 09:01:02 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 16:26:30 UTC