If one handler changes the state who knows what will happen. I guess the order in which handers are called is significant. That's one advantage to using a function like "all" to compose callbacks - its very clear what order they get called in. You could call it 'sequence' to make it even clearer (that they are called one at a time left to right, not in parallel).
You could make the callback an optional parameter, and use it if supplied, and return an object (for the existing API if none is supplied). Cheers, Keean. On 11 January 2011 09:31, Axel Rauschmayer <a...@rauschma.de> wrote: > Looks great, I just tried to stay as close to the current API as possible. > > A single handler should definitely be enough. Can, say, a cursor be read > multiple times (if there are several success handlers)? Doesn’t that make > things more complicated? > > On Jan 11, 2011, at 10:22 , Keean Schupke wrote: > > Comments inline: > > On 11 January 2011 07:11, Axel Rauschmayer <a...@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 <jo...@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 <ke...@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 <ke...@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 <ke...@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 <jo...@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.mozi...@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 <jo...@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 < >>> jor...@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 < >>> jor...@chromium.org> >>> >>>> >>> wrote: >>> >>>> >>>> >>> >>>> >>>> On Tue, Nov 9, 2010 at 11:35 AM, Jonas Sicking < >>> jo...@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.rauschma...@ifi.lmu.de >> http://hypergraphs.de/ >> ### Hyena: organize your ideas, free at hypergraphs.de/hyena/ >> >> >> >> > > -- > Dr. Axel Rauschmayer > axel.rauschma...@ifi.lmu.de > http://hypergraphs.de/ > ### Hyena: organize your ideas, free at hypergraphs.de/hyena/ > > > >