W3C home > Mailing lists > Public > public-webapps@w3.org > January to March 2011

Re: [IndexedDB] Events and requests

From: Jonas Sicking <jonas@sicking.cc>
Date: Mon, 10 Jan 2011 14:31:11 -0800
Message-ID: <AANLkTim6x+N-aq6eXGVQSMz2BKDr2PqrYe=N83WYFBnW@mail.gmail.com>
To: Keean Schupke <keean@fry-it.com>
Cc: ben turner <bent.mozilla@gmail.com>, Jeremy Orlow <jorlow@chromium.org>, Webapps WG <public-webapps@w3.org>
This seems like something better suggeseted to the lists at ECMA where
javascript (or rather ECMAScript) is being standardized. I hardly
think that a database API like indexedDB is the place to redefine how
javascript should handle asynchronous programming.

/ Jonas

On Mon, Jan 10, 2011 at 2:26 PM, Keean Schupke <keean@fry-it.com> wrote:
> Just to correct my cut and paste error, that was of course supposed to be:
> var y = do {
>     result1 <- db.transaction(["foo"]).objectStore("foo").getM(mykey1);
>     result2 <- db.transaction(["foo"]).objectStore("foo").getM(mykey2);
>     unit(result1 + result2);
> }
>
> Cheers,
> Keean.
> On 10 January 2011 22:24, Keean Schupke <keean@fry-it.com> wrote:
>>
>> Okay, sorry, the original change seemed sensible, I guess I didn't see how
>> you got from there to promises.
>>
>> Here's some fun to think about as an alternative though:
>>
>> Interestingly the pattern of multiple callbacks, providing each callback
>> is passed zero or one parameter forms a Monad.
>> So for example if 'unit' is the constructor for the object returned from
>> "get" then onsuccess it 'bind' and I can show that these obey the 3 monad
>> laws. Allowing composability of callbacks. So you effectively have:
>> var x = db.transaction(["foo"]).objectStore("foo").getM(mykey);
>> var y =
>> db.transaction(["foo"]).objectStore("foo").getM(mykey1).bind(function(result1)
>> {
>>
>>  db.transaction(["foo"]).objectStore("foo").getM(mykey2).bind(function(result2)
>> {
>>         unit(result1 + result2);
>>     });
>> });
>> The two objects returned "x" and "y" are both the same kind of object. y
>> represents the sum or concatination of the results of the lookups "mykey1"
>> and "mykey2". You would use it identically to using the result of a single
>> lookup:
>> x.bind(function(result) {... display the result of a single lookup ...});
>> y.bind(function(result) {... display the result of both lookups ...});
>>
>> If we could then have some syntactic sugar for this like haskell's do
>> notation we could write:
>> var y = do {
>>     db.transaction(["foo"]).objectStore("foo").getM(mykey1);
>>     result1 <- db.transaction(["foo"]).objectStore("foo").getM(mykey2);
>>     result2 <- db.transaction(["foo"]).objectStore("foo").getM(mykey2);
>>     unit(result1 + result2);
>> }
>> Which would be a very neat way of chaining callbacks...
>>
>> Cheers,
>> Keean.
>>
>> On 10 January 2011 22:00, Keean Schupke <keean@fry-it.com> wrote:
>>>
>>> Whats wrong with callbacks? To me this seems an unnecessary complication.
>>> Presumably you would do:
>>> var promise = db.transaction(["foo"]).objectStore("foo").get(mykey);
>>> var result = promise.get();
>>> if (!result) {
>>>     promise.onsuccess(function(res) {...X...});
>>> } else {
>>>     ...Y...
>>> }
>>>
>>> So you end up having to duplicate code at X and Y to do the same thing
>>> directly or in the context of a callback. Or you define a function to
>>> process the result:
>>> var f = function(res) {...X...};
>>> var promise = db.transaction(["foo"]).objectStore("foo").get(mykey);
>>> var result = promise.get();
>>> if (!result) {
>>>     promise.onsuccess(f);
>>> } else {
>>>     f(result)
>>> };
>>> But in which case what advantage does all this extra clutter offer over:
>>>
>>> db.transaction(["foo"]).objectStore("foo").get(mykey).onsuccess(function(res)
>>> {...X...});
>>>
>>> I am just wondering whether the change is worth the added complexity?
>>>
>>> Cheers,
>>> Keean.
>>>
>>> On 10 January 2011 21:31, Jonas Sicking <jonas@sicking.cc> wrote:
>>>>
>>>> I did some outreach to developers and while I didn't get a lot of
>>>> feedback, what I got was positive to this change.
>>>>
>>>> The basic use-case that was brought up was implementing a promises
>>>> which, as I understand it, works similar to the request model I'm
>>>> proposing. I.e. you build up these "promise" objects which represent a
>>>> result which may or may not have arrived yet. At some point you can
>>>> either read the value out, or if it hasn't arrived yet, register a
>>>> callback for when the value arrives.
>>>>
>>>> It was pointed out that this is still possible with how the spec is
>>>> now, but it will probably result in that developers will come up with
>>>> conventions to set the result on the request themselves. This wouldn't
>>>> be terribly bad, but also seems nice if we can help them.
>>>>
>>>> / Jonas
>>>>
>>>> On Mon, Jan 10, 2011 at 8:13 AM, ben turner <bent.mozilla@gmail.com>
>>>> wrote:
>>>> > FWIW Jonas' proposed changes have been implemented and will be
>>>> > included in Firefox 4 Beta 9, due out in a few days.
>>>> >
>>>> > -Ben
>>>> >
>>>> > On Fri, Dec 10, 2010 at 12:47 PM, Jonas Sicking <jonas@sicking.cc>
>>>> > wrote:
>>>> >> I've been reaching out to get feedback, but no success yet. Will
>>>> >> re-poke.
>>>> >>
>>>> >> / Jonas
>>>> >>
>>>> >> On Fri, Dec 10, 2010 at 4:33 AM, Jeremy Orlow <jorlow@chromium.org>
>>>> >> wrote:
>>>> >>> Any additional thoughts on this?  If no one else cares, then we can
>>>> >>> go with
>>>> >>> Jonas' proposal (and we should file a bug).
>>>> >>> J
>>>> >>>
>>>> >>> On Thu, Nov 11, 2010 at 12:06 PM, Jeremy Orlow <jorlow@chromium.org>
>>>> >>> wrote:
>>>> >>>>
>>>> >>>> On Tue, Nov 9, 2010 at 11:35 AM, Jonas Sicking <jonas@sicking.cc>
>>>> >>>> wrote:
>>>> >>>>>
>>>> >>>>> Hi All,
>>>> >>>>>
>>>> >>>>> One of the things we briefly discussed at the summit was that we
>>>> >>>>> should make IDBErrorEvents have a .transaction. This since we are
>>>> >>>>> allowing you to place new requests from within error handlers, but
>>>> >>>>> we
>>>> >>>>> currently provide no way to get from an error handler to any
>>>> >>>>> useful
>>>> >>>>> objects. Instead developers will have to use closures to get to
>>>> >>>>> the
>>>> >>>>> transaction or other object stores.
>>>> >>>>>
>>>> >>>>> Another thing that is somewhat strange is that we only make the
>>>> >>>>> result
>>>> >>>>> available through the success event. There is no way after that to
>>>> >>>>> get
>>>> >>>>> it from the request. So instead we use special event interfaces
>>>> >>>>> with
>>>> >>>>> supply access to source, transaction and result.
>>>> >>>>>
>>>> >>>>> Compare this to how XMLHttpRequests work. Here the result and
>>>> >>>>> error
>>>> >>>>> code is available on the request object itself. The 'load' event,
>>>> >>>>> which is equivalent to our 'success' event didn't supply any
>>>> >>>>> information until we recently added progress event support. But
>>>> >>>>> still
>>>> >>>>> it only supplies information about the progress, not the actual
>>>> >>>>> value
>>>> >>>>> itself.
>>>> >>>>>
>>>> >>>>> One thing we could do is to move
>>>> >>>>>
>>>> >>>>> .source
>>>> >>>>> .transaction
>>>> >>>>> .result
>>>> >>>>> .error
>>>> >>>>>
>>>> >>>>> to IDBRequest. Then make "success" and "error" events be simple
>>>> >>>>> events
>>>> >>>>> which only implement the Event interface. I.e. we could get rid of
>>>> >>>>> the
>>>> >>>>> IDBEvent, IDBSuccessEvent, IDBTransactionEvent and IDBErrorEvent
>>>> >>>>> interfaces.
>>>> >>>>>
>>>> >>>>> We'd still have to keep IDBVersionChangeEvent, but it can inherit
>>>> >>>>> Event directly.
>>>> >>>>>
>>>> >>>>> The request created from IDBFactory.open would return a IDBRequest
>>>> >>>>> where .transaction and .source is null. We already fire a IDBEvent
>>>> >>>>> where .source is null (actually, the spec currently doesn't define
>>>> >>>>> what the source should be I see now).
>>>> >>>>>
>>>> >>>>>
>>>> >>>>> The only major downside with this setup that I can see is that the
>>>> >>>>> current syntax:
>>>> >>>>>
>>>> >>>>> db.transaction(["foo"]).objectStore("foo").get(mykey).onsuccess =
>>>> >>>>> function(e) {
>>>> >>>>>  alert(e.result);
>>>> >>>>> }
>>>> >>>>>
>>>> >>>>> would turn into the slightly more verbose
>>>> >>>>>
>>>> >>>>> db.transaction(["foo"]).objectStore("foo").get(mykey).onsuccess =
>>>> >>>>> function(e) {
>>>> >>>>>  alert(e.target.result);
>>>> >>>>> }
>>>> >>>>>
>>>> >>>>> (And note that with the error handling that we have discussed, the
>>>> >>>>> above code snippets are actually plausible (apart from the alert()
>>>> >>>>> of
>>>> >>>>> course)).
>>>> >>>>>
>>>> >>>>> The upside that I can see is that we behave more like
>>>> >>>>> XMLHttpRequest.
>>>> >>>>> It seems that people currently follow a coding pattern where they
>>>> >>>>> place a request and at some later point hand the request to
>>>> >>>>> another
>>>> >>>>> piece of code. At that point the code can either get the result
>>>> >>>>> from
>>>> >>>>> the .result property, or install a onload handler and wait for the
>>>> >>>>> result if it isn't yet available.
>>>> >>>>>
>>>> >>>>> However I only have anecdotal evidence that this is a common
>>>> >>>>> coding
>>>> >>>>> pattern, so not much to go on.
>>>> >>>>
>>>> >>>> Here's a counter proposal:  Let's add .transaction, .source, and
>>>> >>>> .result
>>>> >>>> to IDBEvent and just specify them to be null when there is no
>>>> >>>> transaction,
>>>> >>>> source, and/or result.  We then remove readyState from IDBResult as
>>>> >>>> it
>>>> >>>> serves no purpose.
>>>> >>>> What I'm proposing would result in an API that's much more similar
>>>> >>>> to what
>>>> >>>> we have at the moment, but would be a bit different than XHR.  It
>>>> >>>> is
>>>> >>>> definitely good to have similar patterns for developers to follow,
>>>> >>>> but I
>>>> >>>> feel as thought the model of IndexedDB is already pretty different
>>>> >>>> from XHR.
>>>> >>>>  For example, method calls are supplied parameters and return an
>>>> >>>> IDBRequest
>>>> >>>> object vs you using new to create the XHR object and then making
>>>> >>>> method
>>>> >>>> calls to set it up and then making a method call to start it.  In
>>>> >>>> fact, if
>>>> >>>> you think about it, there's really not that much XHR and IndexedDB
>>>> >>>> have in
>>>> >>>> common except that they use event handlers.
>>>> >>>> As for your proposal, let me think about it for a bit and forward
>>>> >>>> it on to
>>>> >>>> some people I know who are playing with IndexedDB already.
>>>> >>>> J
>>>> >>>
>>>> >>
>>>> >>
>>>> >
>>>>
>>>
>>
>
>
Received on Monday, 10 January 2011 22:32:05 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 18:49:42 GMT