Re: [IndexedDB] Events and requests

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
> >>>> >>>
> >>>> >>
> >>>> >>
> >>>> >
> >>>>
> >>>
> >>
> >
> >
>

Received on Monday, 10 January 2011 22:41:28 UTC