- 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