- From: Keean Schupke <keean@fry-it.com>
- Date: Mon, 10 Jan 2011 22:26:28 +0000
- To: Jonas Sicking <jonas@sicking.cc>
- Cc: ben turner <bent.mozilla@gmail.com>, Jeremy Orlow <jorlow@chromium.org>, Webapps WG <public-webapps@w3.org>
- Message-ID: <AANLkTimepQ_EnTDRRXsE+PWn3FCJNqvh_AyZH04x9cDR@mail.gmail.com>
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:27:02 UTC