- From: Keean Schupke <keean@fry-it.com>
- Date: Tue, 11 Jan 2011 09:22:12 +0000
- To: Axel Rauschmayer <axel@rauschma.de>
- Cc: Jonas Sicking <jonas@sicking.cc>, ben turner <bent.mozilla@gmail.com>, Jeremy Orlow <jorlow@chromium.org>, Webapps WG <public-webapps@w3.org>
- Message-ID: <AANLkTimTYBLxQ0Uh1ZR48+F9TuayfCOv__CcVhwaCvUN@mail.gmail.com>
Comments inline: On 11 January 2011 07:11, Axel Rauschmayer <axel@rauschma.de> wrote: > Coming back to the initial message in this thread (at the very bottom): > => General rule of thumb: clearly separate input data and output data. > > Using JavaScript dynamic nature, things could look as follows: > > indexedDB.open('AddressBook', 'Address Book', { > success: function(evt) { > }, > error: function(evt) { > } > }); > Personally I prefer a single callback passed an object. indexedDB.open('AddressBook', 'Address Book', function(event) { switch(event.status) { case EVENT_SUCCESS: .... break; case EVENT_ERROR: .... break; } }); As it allows callbacks to be composed more easily. - The last argument is thus the request and clearly input. > > - If multiple success handlers are needed, success could be an array of > functions (same for error handlers). > multiple handlers can be passes using a composition function: // can be defined in the library var all = function(flist) { return function(event) { for (int i = 0; i < flist.length; i++) { flist[i](event); } }; }; indexedDB.open('AddressBook', 'Address Book', all([fn1, fn2, fn3])); Cheers, Keean. > - I would eliminiate readyState and move abort() to IDBEvent (=output and > an interface to the DB client). > > - With subclasses of IDBEvent one has the choice of eliminating them by > making their fields additional parameters of success() and error(). > event.result is a prime candidate for this! > > - This above way eliminates the need of manipulating the request *after* (a > reference to) it has been placed in the event queue. > > Questions: > > - Is it really necessary to make IDBEvent a subclass of Event and thus drag > the DOM (which seems to be universally hated) into IndexedDB? > > - Are there any other asynchronous DB APIs for dynamic languages that one > could learn from (especially from mistakes that they have made)? They must > have design principles and rationales one might be able to use. WebDatabase > (minus schema plus cursor) looks nice. > > On Jan 10, 2011, at 23:40 , Keean Schupke wrote: > > Hi, > > I did say it was for fun! If you think it should be suggested somewhere I > am happy to do so. Note that I renamed 'onsuccess' to 'bind' to show how it > works as a monad, there is no need to do this (although I prefer to it to > explicitly show it is a Monad). > > The definition of unit is simply: > > var unit = function(v) { > return { > onsuccess: function(f) {f(v);} > }; > }; > > And then you can compose callbacks using 'onsuccess'... > > you might like to keep onsuccess, and use "result" instead of "unit"... So > simply using the above definition you can compose callbacks: > > var y = > db.transaction(["foo"]).objectStore("foo").getM(mykey1).onsuccess(function(result1) > { > > db.transaction(["foo"]).objectStore("foo").getM(mykey2).onsuccess(function(result2) > { > result(result1 + result2); > }); > }); > > > Cheers, > Keean. > > > On 10 January 2011 22:31, Jonas Sicking <jonas@sicking.cc> wrote: > >> 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 >> >>>> >>> >> >>>> >> >> >>>> >> >> >>>> > >> >>>> >> >>> >> >> >> > >> > >> > > > -- > Dr. Axel Rauschmayer > Axel.Rauschmayer@ifi.lmu.de > http://hypergraphs.de/ > ### Hyena: organize your ideas, free at hypergraphs.de/hyena/ > > > >
Received on Tuesday, 11 January 2011 09:22:45 UTC