- From: Jeremy Orlow <jorlow@chromium.org>
- Date: Thu, 4 Mar 2010 14:44:19 +0000
- To: Kris Zyp <kris@sitepen.com>
- Cc: Joseph Pecoraro <joepeck02@gmail.com>, public-webapps <public-webapps@w3.org>
- Message-ID: <5dd9e5c51003040644n589b7ffo6f473035a53a5339@mail.gmail.com>
On Thu, Mar 4, 2010 at 2:37 PM, Jeremy Orlow <jorlow@chromium.org> wrote:
> On Wed, Mar 3, 2010 at 8:48 PM, Kris Zyp <kris@sitepen.com> wrote:
>
>> -----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> <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.
>>
>
> You are quite right! I misunderstood how this part of promises worked.
>
> Is there excitement about speccing promises in general? If not, it seems a
> little odd to spec such a powerful mechanism into just IndexedDB....and it
> might be best to spec the simplified version of .then(): .then() will return
> undefined, onsuccess/onerror's return values will be swallowed, and any
> thrown exceptions will be thrown.
>
Er....thrown exceptions will be _swallowed_ (not thrown).
> This should make it easy to make IndexedDB support full blown promises
> if/whenever they're specced. (It's not clear to me whether UA support for
> them would offer enough advantages to warrant it.)
>
> It sounds like you're OK with such an approach, Kris?
>
> What do others think?
>
> J
>
>
>> >
>> > > 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 Thursday, 4 March 2010 14:45:11 UTC