- From: Eric Rescorla <ekr@rtfm.com>
- Date: Thu, 2 Oct 2014 04:34:25 -0700
- To: Silvia Pfeiffer <silviapfeiffer1@gmail.com>
- Cc: Jan-Ivar Bruaroey <jib@mozilla.com>, "public-media-capture@w3.org" <public-media-capture@w3.org>
- Message-ID: <CABcZeBOdatfr3GeCdznV8LjHoPmCKLy=j-OmhJhM8gL=xSzDsw@mail.gmail.com>
On Wed, Oct 1, 2014 at 11:47 PM, Silvia Pfeiffer <silviapfeiffer1@gmail.com> wrote: > Jan-Ivar, > > Are you saying that Mozilla is blocked on moving forward with a more > complete implementation of the webrtc spec because the spec is not using > promises? > > I'm keen for Mozilla to be completely feature compatible with chrome and > would prefer Mozilla devs spent time doing that over worrying about > promises. But if it's promises that's holding you up, I'm more inclined to > accept them. > Promises are holding us up in that time spent arguing about promises is time spent not writing software. Other than that, we're not blocked on this, no. -Ekr > Best Regards, > Silvia. > On 2 Oct 2014 06:06, "Jan-Ivar Bruaroey" <jib@mozilla.com> 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")); >> >> This outputs: >> >> success1 >> success2a >> fail3 >> success4 >> success5 >> >> Importantly, fail3 - not fail2 - is called because success2 which barfed >> is actually step3. >> >> Furthermore, success4 and success5 proceed because we "caught" the error >> in fail3! (we'd need to re-throw the error just like we'd do in a try-catch >> clause if we didn't want that). >> >> Now, here's the same test in a fiddle [3] showing how callbacks handle >> errors poorly (spot the bug): >> >> >> <div id="log"></div> >> >> var div = document.getElementById("log"); >> var log = msg => (div.innerHTML = div.innerHTML + msg + "<br>"); >> >> try { >> oldcall(() => { >> log("success1"); >> oldcall(() => { >> log("success2a"); >> barf; >> log("success2b"); >> oldcall(() => { >> log("success3"); >> oldcall(() => { >> log("success4"); >> oldcall(() => { >> log("success5"); >> }, () => log("fail5")); >> }, () => log("fail4")); >> }, () => log("fail3")); >> }, () => log("fail2")); >> }, () => log("fail1")); >> } catch(e) { log("failure"); } >> >> function oldcall(success, failure) { >> var succeed = true; >> setTimeout(succeed? success : failure, 0); >> } >> >> This outputs: >> >> success1 >> success2a >> >> >> and a "ReferenceError: barf is not defined" in web console. >> >> This is bad because it means the program can't handle the error. The bug? >> try/catch is needed around success2 and - to be safe - around *every* >> callback! This final fiddle [4] shows what's minimally needed to handle >> errors safely in our API, and even this doesn't truly propagate errors, >> e.g. we're just pushing handlers in, not allowing errors to rise up: >> >> >> var div = document.getElementById("log"); >> var log = msg => (div.innerHTML = div.innerHTML + msg + "<br>"); >> >> try { >> oldcall(() => { try { >> log("success1"); >> oldcall(() => { try { >> log("success2a"); >> barf; >> log("success2b"); >> oldcall(() => { try { >> log("success3"); >> oldcall(() => { try { >> log("success4"); >> oldcall(() => { try { >> log("success5"); >> } catch(e) { log("failure"); } }, () => log("fail5")); >> } catch(e) { log("fail5"); } }, () => log("fail4")); >> } catch(e) { log("fail4"); } }, () => log("fail3")); >> } catch(e) { log("fail3"); } }, () => log("fail2")); >> } catch(e) { log("fail2"); } }, () => log("fail1")); >> } catch(e) { log("failure"); } >> >> function oldcall(success, failure) { >> var succeed = true; >> setTimeout(succeed? success : failure, 0); >> } >> >> This error-prone boilerplate is reminiscent of antique languages without >> exception-handling, and has this output: >> >> success1 >> success2a >> fail3 >> >> This STILL doesn't have the same output as the first fiddle, and I gave >> up since I don't think a version that does is feasible without promises. >> >> Could we at least hide the boilerplate inside oldcall? We could, but this >> would require a third argument (recall that we want fail3 not fail2): >> >> >> function oldcall(success, failure, followingFailure) { >> try { >> var succeed = true; >> setTimeout(succeed? success : failure, 0); >> } catch (e) { followingFailure(e); } >> } >> >> The third argument would have to be optional to be backwards compatible, >> so there would be no enforcement. Even then it would be complicated to use, >> and still wouldn't solve propagation. >> >> The proper way to solve this is with promises. >> >> .: Jan-Ivar :. >> >> [1] >> http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/#what-is-the-point-of-promises >> [2] Firefox: http://jsfiddle.net/jib1/eyq80vh6 - Others: >> http://jsfiddle.net/jib1/68h59cbf >> [3] Firefox: http://jsfiddle.net/jib1/79qLeb4g - Others: >> http://jsfiddle.net/jib1/qyqypsyv >> [4] Firefox: http://jsfiddle.net/jib1/cy4rvpLb - Others: >> http://jsfiddle.net/jib1/xh5r933a >> >>
Received on Thursday, 2 October 2014 11:35:34 UTC