W3C home > Mailing lists > Public > public-html-comments@w3.org > June 2008

RE: DOM Storage feedback

From: Ian Hickson <ian@hixie.ch>
Date: Fri, 20 Jun 2008 07:16:39 +0000 (UTC)
To: Zhenbin Xu <Zhenbin.Xu@microsoft.com>
Cc: Ian Hickson <IMCEAMAILTO-ian+40hixie+2Ech@windows.microsoft.com>, "public-html-comments@w3.org" <public-html-comments@w3.org>, "public-html@w3.org" <public-html@w3.org>, Sunava Dutta <sunavad@windows.microsoft.com>, IE8 Core AJAX SWAT Team <ieajax@microsoft.com>
Message-ID: <Pine.LNX.4.62.0806200639230.13974@hixie.dreamhostps.com>

On Thu, 19 Jun 2008, Zhenbin Xu wrote:
> >
> > The idea is that the storage is asynchronous (and under the control of 
> > the UA), but that the API appears synchronous. That is, as soon as one 
> > script sets a storage item to a particular value, the setter will 
> > return with no latency, and all attempts from any frames to read that 
> > same storage item will give the new value back. It doesn't have to be 
> > stored to disk immediately, however. Since this is a simple name/value 
> > text-only API, it is trivially cached in memory.
> 
> Interesting - this is precisely what we meant by async model - storage 
> is asynchronous but the API appears synchronous.  Either the spec has 
> changed or we may have read the spec incorrectly? Previously the spec 
> stated that the setItem operation has to guarantee atomic operation, 
> which we read it to mean guaranteed to be on persist store (disk or 
> cloud).  If this is not the case, then we are in agreement here.  Since 
> we thought we differed from the spec, we added the event to compensate 
> for it.  We don't feel it is important event but it may be useful.

The spec does say things have to be atomic from the perspective of the 
scripts, but doesn't attempt to say anything about how that should be 
implemented. Would you like this made more explicit?


> > Also, if the main use case is caching data, then it is reasonably easy 
> > to use the simple API to provide a transaction-like mechanism:
> >
> >    // run this first, in one script block
> >    var id = localStorage['last-id'] + 1;
> >    localStorage['last-id'] = id;
> >    localStorage['email-ready-' + id] = "0"; // "begin"
> >
> >    // these can run each in separate script blocks as desired
> >    localStorage['email-subject-' + id] = subject;
> >    localStorage['email-from-' + id] = from;
> >    localStorage['email-to-' + id] = to;
> >    localStorage['email-body-' + id] = body;
> >
> >    // run this last
> >    localStorage['email-ready-' + id] = "1"; // "commit"
> >
> > An author could easily wrap this up providing custom begin() and 
> > commit() functions if desired. I don't see what having them in the API 
> > gains us, really, other than extra complexity for UAs.
> 
> Here is the concern:
> 
>      localStorage['email-subject-' + id] = getSubject();
>      localStorage['email-from-' + id] = resolveEmailias(from);
>      localStorage['email-to-' + id] = resolveEmailalis(to);
>      localStorage['email-body-' + id] = getBody();
> 
> If resolveEmailAlias throws an exception, we would have partially 
> persisted states. Yes you can work around it by assigning results to 
> temporary variables but it is simpler to provide begin() and commit() to 
> the page author, especially considering there can be complex logic 
> between states. E.g
>
>      localStorage.begin();
>      Component1FromDeveloper1();     // local storage operation inside
>      Component2FromDeveloper2();     // local storage operation inside
>      localStorage.commit();
>
> Having begin/commit would allow better distributed, componentized 
> development.

But the problem is that the above would fail if the component from 
developer 2 uses .begin() or .commit(), or if an exception was raised and 
caught elsewhere (since the .commit() would be missed and now everything 
would be covered by the "transaction"), etc. Basically having just a 
single transaction state with two separate methods and nothing based on an 
object's lifetime or anything to guarantee consistency the API becomes 
unusable in any number of ways.

Right now you can avoid all these problems by just doing:   

   var success = false;
   try {
     var id = localStorage['last-id'] + 1;
     localStorage['last-id'] = id;
     localStorage['email-ready-' + id] = "0"; // "begin"

     localStorage['email-subject-' + id] = getSubject();
     localStorage['email-from-' + id] = resolveEmailAlias(from);
     localStorage['email-to-' + id] = resolveEmailAlias(to);
     localStorage['email-body-' + id] = getBody();

     success = true;
   } catch (e) {
     // report or resolve e if necessary
   }
   if (success) {
     localStorage['email-ready-' + id] = "1"; // "commit"
   } else {
     localStorage.removeItem('email-subject-' + id);
     localStorage.removeItem('email-from-' + id);
     localStorage.removeItem('email-to-' + id);
     localStorage.removeItem('email-body-' + id);
     localStorage.removeItem('email-ready-' + id); // "cancel"
   }

This can easily be wrapped into a utility method that takes a single 
method as its only argument:

   function transactionAdd(body) {
     var success = false;
     var keys = [];
     try {
       var id = localStorage['last-id'] + 1;
       localStorage['last-id'] = id;
       localStorage['block-' + id] = "0"; // "begin"

       body(function (name, value) {
         localStorage['block-' + id + '-' + name] = value;
         keys.push(name);
       });

       success = true;
     } catch (e) {
       // report or resolve e if necessary
     }
     if (success) {
       localStorage['block-' + id] = "1"; // "commit"
     } else {
       for (var i in keys)
         localStorage.removeItem('block-' + id + '-' + keys[i]);
       localStorage.removeItem('block-' + id); // "cancel"
     }
     return success;
   }

You would use this like this:

   transactionAdd(function(add) {
     add('subject', getSubject());
     add('from', resolveEmailAlias(from));
     add('to', resolveEmailAlias(to));
     add('body', getBody());
   });

In short I don't really see the need for an explicit .begin() or .commit() 
feature in the API.


(Also, I'd like to reiterate my earlier request that the non-standard 
event and methods that you implemented in IE8 either be removed or be 
clearly prefixed with "ms" or some such to indicate that they are 
proprietary extensions -- we don't want to confuse authors on the platform 
who might test in one browser and find things don't work in another 
browser. Thanks!)

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'
Received on Friday, 20 June 2008 07:17:25 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Wednesday, 1 June 2011 00:13:58 GMT