Re: [IndexedDB] Promises (WAS: Seeking pre-LCWD comments for Indexed Database API; deadline February 2)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
 


On 3/3/2010 4:01 AM, Jeremy Orlow wrote:
> On Wed, Mar 3, 2010 at 4:49 AM, Kris Zyp <kris@sitepen.com
> <mailto:kris@sitepen.com>> wrote:
> [snip]
>
>     > The promises would only have a
>     "then" method which would take in an
>
>     > onsuccess and onerror callback.  Both are optional.  The
>     onsuccess
>
>     > function should take in a single parameter which matches the
>     return
>
>     > value of the synchronous counterpart.  The onerror function
>     should
>
>     > take in an IDBDatabaseError.  If the callbacks are null,
>     undefined,
>
>     > or omitted, they're ignored.  If they're anything else, we should
>
>     > probably either raise an exception immediately or ignore them.
>
>     Yes.
>
>
> Any thoughts on whether we'd raise or ignore improper inputs?  I'm
> leaning towards raise since it would be deterministic and silently
> ignoring seems like a headache from a developer standpoint.
Throwing an error on improper inputs is fine with me.
> 
>
>     > If there's an error, all onerror
>     callbacks would be called with the
>
>     > IDBDatabaseError.
>
>     Yes.
>
>
>     > Exceptions within callbacks
>     would be ignored.
>
>     With CommonJS promises, the promise returned by the then() call goes
>     into an error state if a callback throws an exception. For example,
>
>     someAsyncOperation.then(successHandler, function(){ throw new
>     Error("test") })
>         .then(null, function(error){ console.log(error); });
>
>     Would log the thrown error, effectively giving you a way of catching
>     the error.
>
>     Are you suggesting this as a simplification so that IndexedDB impls
>     doesn't have to worry about recursive creation of promises? If so, I
>     suppose that seems like a reasonable simplification to me.
>     Although if
>     promises are something that could be potentially reused in other
>     specs, it would be nice to have a quality solution, and I don't
>     think
>     this is a big implementation burden, I've implemented the recursive
>     capabilities in dozen or two lines of JS code. But if burden is too
>     onerous, I am fine with the simplification.
>
>
> When you say "recursive capabilities" are you just talking about how
> to handle exceptions, or something more?
>
> In terms of exceptions: I don't think it's an
> enormous implementational burden and thus I think it's fine to
> ignore that part of the equation.  So the question mainly comes down
> to whether the added complexity is worth it.  Can you think of any
> real-world examples of when this capability is useful in promises?
>  If so, that'd definitely help us understand the pro's and con's.

Maybe I misunderstanding your suggestion. By "recursive capability" I
meant having then() return a promise (that is fulfilled with the
result of executing the callback), and I thought you were suggesting
that instead, then() would not return a promise. If then() returns a
promise, I think the returned promise should clearly go into an error
state if the callback throws an error. The goal of promises is to
asynchronously model computations, and if a computation throws, it
should result in the associated promise entering error state. The
promise returned by then() exists to represent the result of the
execution of the callback, and so it should resolve to the value
returned by the callback or an error if the callback throws. Silenty
swallowing errors seems highly undesirable.

Now if we are simplifying then() to not return a promise at all, than
I would think callbacks would just behave like any other event
listener in regards to uncaught errors.
>
>     > In terms of speccing, I'm not sure if we can get away with
>     speccing
>
>     > one promise interface or whether we'd need to create one for each
>
>     > type of promise.
>
>     Certainly the intent of promises is that there is exists only one
>     generic promise interface that can be reused everywhere, at
>     least from
>     the JS perspective, not sure if the extra type constraints in IDL
>     demand multiple interfaces to model promise's effectively
>     parameterized generic type form.
>
>
> Unfortunately, I don't really know.  Before we try speccing it, I'll
> definitely see if any WebIDL experts have suggestions.
>
>
> Also, do we want to explicitly spec what happens in the following case?
>
> window.indexedDB.open(...).then(
>     function(db) {  db.openObjectStore("a").then( function(os) {
> alert("Opened a"); } ) }
> ).then(
>     function(db) { alert("Second db opened"); }
> );
>
> Clearly the first function(db) is called first.  But the question is
> whether it'd be a race of which alert is called first or whether the
> "Second db opened" alert should always be shown first (since clearly
> if the first is called, the second _can_ be fired immediately
> afterwards).
>
> I'm on the fence about whether it'd be useful to spec that the
> entire chain needs to be called one after the other before calling
> any other callbacks.  Does anyone have thoughts on whether this is
> useful or not?  If we do spec it to call the entire chain, then what
> happens if inside one of the callbacks, something is added to the
> chain (via another .then() call).
>
Specing the order of multiple events in the event loop seems like it
would be excessive burden on implementors, IMO.

> I've been talking to a co-worker here who seems to know a decent
> amount about promises (as implemented in E) and some about differed
> (as implemented in Python's Twisted library).  From talking to him,
> it seems that my original suggestion for not handling exceptions
> thrown inside a .then() callback is the way to go.
>
> It seems as though promises put a lot of weight on composability and
> making it so that the order of .then() calls not mattering.  This
> means that you can then pass promises to other async interfaces and
> not have to worry about different timings leading to different
> results.  It also means that if you pass a promise into multiple
> consumers (say, javascript libraries) you don't need to worry about
> one using a promise in a way that screws up another.
>
> Differed seems to be more expressive and flexible.  For example,
> instead of doing this:
>
> window.indexedDB.open(...).then(
>     function(db) {  db.openObjectStore("a").then(
>         function(os) { os.get("x").then(
>             function(value) { alert("Value: " + value); }
>         ) }
>     ) }
> );
>
> I could do this:
>
> window.indexedDB.open(...).then(
>     function(db) { return db.openObjectStore("a"); }    // Note the
> return value is passed on to the next step.
> ).then(
>     function(os) { return os.get("x"); }
> ).then(
>     function(value) { alert("Value: " + value); }
> );
>
> And I can also have any one of those throw an error which would be
> passed on to the rest of the chain, much like nested try/catch
> blocks in single threaded code.

Yes, this type of chaining is exactly the intent of the promises I
have suggested. Having the return value of the thrown error passed on
to the rest of the chain is most intuitive IMO (since it is analagous
to synchronous call stacks) and what I would recommend. One thing that
Twisted got wrong (but E got right) was that the callbacks in
Twisted's deferred mutate the original deferred itself which causes
difficult to contain side-effects, whereas the callback's return value
should only affect a newly generated returned promise (maintaining
proper functional data flow of outputs not affecting inputs).

- -- 
Kris Zyp
SitePen
(503) 806-1841
http://sitepen.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
 
iEYEARECAAYFAkuOywcACgkQ9VpNnHc4zAzWCQCgiTl7S2Iv477vuzH2+IG6raMc
jrsAoL4aUfbG7WWHUktwbid8MMw0C5C6
=PVcB
-----END PGP SIGNATURE-----

Received on Wednesday, 3 March 2010 20:48:47 UTC