[dojo-contributors] Promises

Mike Wilcox mike at mikewilcox.net
Fri Mar 5 06:56:58 EST 2010

Like Bill, I too am not following closely (at least not examining the code for efficiency). But I want to bring up that it was I who pointed out some time back that Deferred was causing misleading errors. This misdirection is from using a Deferred during development when, in my case, I hadn't yet attached my onError handlers, because error handling is something I do at a certain time. Note the solution was/is to *always* use an onError handler, but this is not something that is simple to grasp.

Without an onError handler, the XHR call could return the onCallback, but you may have a runtime error in your code further down the line that is processed as a result of the callback, and that error was often a cryptic "TypeError" or something. You knew there was an error, but you didn't know *where* it was because it was caught in the last try-catch - that of the Deferred.

And you know, without running some tests, I think that situation still exists, even with an onError attached, and even though Kris has made an attempt to fix this at one time. Because I know a lot of my code uses timeouts in deferred callbacks.

So to me, a setTimeout is an elegant solution (and the only one), since the call is async anyway and an extra millisecond is inconsequential, and it keeps the Deferred/Promise from capturing the development code.

I hope that helped and was on point.


On Mar 5, 2010, at 1:10 AM, Bill Keese wrote:

> Not sure I'm following all of this, but how about just doing the 
> setTimeout() in debug mode, and in production mode don't report errors 
> at all unless there's an addErrback() function registered?
> I just can't see a better way, like Kris said even for a simple:
>    dojo.xhr(...).then(callback, errback)
> the error might occur before the then() executes.
> Eugene Lazutkin wrote:
>> Hash: SHA1
>> If it is 100% backwards compatible yet smaller --- I am all for it. But
>> setTimeout() hack bums me out. It is bad on so many levels including
>> debugging. And throwing an error from a setTimeout() callback... What is
>> the grand idea anyway? Who is going to catch it? How to catch it? Why
>> bother? How can it help me to find the source of an error? Why not
>> console.log() an error --- the effect looks similar? Why is this a good
>> way to do the default error processing?
>> Speaking about the (default) error processing.
>> 1) The exception processing is not a symmetric thing. While try/catch
>> are symmetric, throw vs. try/catch are not. I think you make a mistake
>> by wrapping all three together and declaring that all three things
>> always go together. No, they are not. Usually there are many more things
>> that can throw exceptions than places to catch them. The same should be
>> applied to Deferreds/Promises.
>> 2) OK, you caught an error. Now what? Not hypothetically but
>> realistically? Usually you have two major choices:
>> a) Retry an action, or rollback and retry a transaction --- 99% of cases
>> we are talking about (faulty) I/O, which can be treated this way. Try to
>> rollback and repeat "division by 0". :-)
>> b) Tell a user about the problem in some way.
>> Even (a) is frequently done by doing (b) --- asking user, if she wants
>> to repeat an action.
>> How is it done in the Real Life (tm)? You have to have an error
>> reporting facilities in your application. It can be something as simple
>> as using alert(), or (in reality) you have some visual design to present
>> all errors in a uniform way. Even in UI-less applications usually you
>> have a standardized logging or something to this effect.
>> Typically in my apps I have a top-level try-catch, which does exactly
>> what I described above. I rarely catch individual exceptions --- usually
>> splitting them in 2-3 classes (e.g., I/O vs. everything else) are enough
>> for me.
>> Now back to Deferreds/Promises. They don't change anything. But they
>> change an architecture a little bit. One way to do it is to have default
>> global callbacks, which are called when nobody else is handling an
>> error. If I want to be flexible, I use a stack of such callbacks ---
>> only the top one is called. I can push to stack and pop from stack my
>> custom error processors in different phases of my app to have some
>> flexibility.
>> I think it is a reasonable approach. I know from practice that it works.
>> And the technique doesn't rely on any hacks.
>> My proposal is to implement this default callback technique at the
>> library level instead of doing setTimeout() and throw exceptions from
>> nowhere. It doesn't require much code, yet flexible, practical, and
>> works as advertised. ;-)
>> Additionally, for debugging purposes, I think it would be nice to log an
>> error regardless if it was processed or not. From my experience I want
>> to know about it in 99% of the cases, while debugging. Make it a global
>> debugging switch, or reuse an existing one. In any case it is better
>> than to pepper all error callbacks with logging code.
>> Eugene Lazutkin
>> http://lazutkin.com/
>> PS: Is it "tl;dr" case again? Just checking. :-D
>> On 03/03/2010 04:47 PM, James Burke wrote:
>>> On Wed, Mar 3, 2010 at 2:10 PM, Kris Zyp <kzyp at dojotoolkit.org> wrote:
>>>> I put together a backwards compatible variant of this implementation.
>>> This sounds promising. Ha, see what I did there? OK, that already
>>> sounds lame to me after just saying it.
>>> Anyway, I am keen to see this in a 1.5, even better if it meshes with
>>> the CommonJS Promises. However it seemed like the CommonJS version was
>>> still in flux? Kris, how do view the state of the process/progress
>>> over in CommonJS-land? Any possible implementation divergence between
>>> our code and theirs?
>>> I am also not so keen on a setTimeout for the error case. It means we
>>> need a setTimeout implementation for whatever environment runs the
>>> code, and for debugging, it loses the stack trace.
>>> I appreciate that it is a hard thing to sort out. Some
>>> Deferred/Promise use expect errors to just be eaten but other uses
>>> what to know about errors. The basic problem seems to be not knowing
>>> the error intention at the point of calling the reject/errback.
>>> With traditional Dojo use of Deferreds, it seemed almost reasonable to
>>> say if there was no errback listeners when the error is thrown we can
>>> just throw in that case.
>>> However, with a dojo.when(something, callback, errback) sort of
>>> approach, the errback listener is likely not be be bound until after
>>> the something's Deferred may have erred out? I'm thinking out loud a
>>> bit here, maybe Kris or Eugene have other suggestions on how to look
>>> at the problem.
>>> Making sure these things are easy to debug is a pretty big deal. After
>>> spending some time in Twisted land, and dealing with the current state
>>> of dojo.Deferred error debugging, tracking down errors effectively
>>> would make this sort of deferred/async logic a lot easier to get into.
>>> James
>> Version: GnuPG v1.4.9 (GNU/Linux)
>> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
>> iEYEARECAAYFAkuQZO0ACgkQY214tZwSfCugDQCgwKcOCnCct/A0Yit/ZeM+5/ub
>> sHoAnj22a5ksvoakFx/4/5D6ZSjk+vM6
>> =Rw8U
>> -----END PGP SIGNATURE-----
>> _______________________________________________
>> dojo-contributors mailing list
>> dojo-contributors at mail.dojotoolkit.org
>> http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
> _______________________________________________
> dojo-contributors mailing list
> dojo-contributors at mail.dojotoolkit.org
> http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors

More information about the dojo-contributors mailing list