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

Erm...  s/differed/deferred/g

On Wed, Mar 3, 2010 at 4:58 PM, Jeremy Orlow <jorlow@chromium.org> wrote:

> On Wed, Mar 3, 2010 at 11:01 AM, Jeremy Orlow <jorlow@chromium.org> wrote:
>
>> On Wed, Mar 3, 2010 at 4:49 AM, Kris Zyp <kris@sitepen.com> wrote:
>>
>>>  -----BEGIN PGP SIGNED MESSAGE-----
>>> Hash: SHA1
>>>
>>>
>>>
>>> On 3/1/2010 2:52 PM, Jeremy Orlow wrote:
>>> > Thanks for the pointers.  I'm actually pretty sold on the general
>>> > idea of promises, and my intuition is that there won't be a very
>>> > big resource penalty for using an API like this rather than
>>> > callbacks or what's currently specced.  At the same time, it seems
>>> > as though there isn't much of a standard in terms of the precise
>>> > semantics and some of the techniques (such as optionally taking
>>> > callbacks and not returning a promise if they are supplied) seems
>>> > like a decent answer for pure javascript APIs, but maybe not as
>>> > good for IDL and a standard like this.
>>> >
>>> > Do you guys have any recommendations for the precise semantics we'd
>>> > use, if we used promises in IndexedDB?  To get started, let me list
>>> > what I'd propose and maybe you can offer counter proposals or
>>> > feedback on what would or wouldn't work?
>>> >
>>> >
>>> > Each method on a ____Request interface (the async ones in the spec)
>>> > whose counterpart returns something other than void would instead
>>> > return a Promise.
>>>
>>> Asynchronous counterparts to void-returning synchronous functions can
>>> still return promises. The promise would just resolve to undefined,
>>> but it still fulfills the role of indicating when the operation is
>>> complete.
>>>
>>
>> Good point!  Silly me.
>>
>>
>>>  > 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.
>>
>>
>>> > 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.
>>
>
> 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.
>
> I'm not sure if this added flexibility is worth the potentially more
> confusing and subtle code + the increased API surface area, though.  The
> more straightforward composability and predictability of promises also seems
> appealing.
>
> So, at this point, I'm actually leaning towards saying all exceptions
> should just be swallowed up by the then clause.  If a particular
> implementation did want to handle these situations, there's nothing stopping
> them from using a bit of global state to keep track of it (though I admit
> that's less elegent).  It's also worth noting that it'd be possible to
> implement differed semantics with a wrapper around promises.
>
> (Just to reiterate, I am not an expert on any of this, so please correct me
> if I'm misunderstanding/mischaracterizing.  My goal here is to find a
> cleaner way to do the IndexedDB async API.)
>
>
>> > 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).
>>
>> J
>>
>
>

Received on Wednesday, 3 March 2010 17:13:05 UTC