- From: Zhenbin Xu <Zhenbin.Xu@microsoft.com>
- Date: Fri, 20 Jun 2008 11:10:14 -0700
- To: Ian Hickson <ian@hixie.ch>
- 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>
> -----Original Message----- > From: Ian Hickson [mailto:ian@hixie.ch] > Sent: Friday, June 20, 2008 12:17 AM > To: Zhenbin Xu > Cc: Ian Hickson; public-html-comments@w3.org; public-html@w3.org; > Sunava Dutta; IE8 Core AJAX SWAT Team > Subject: RE: DOM Storage feedback > > 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? > > [Zhenbin Xu] More explicit would be nice - especially the synchronous API but asynchronous storage part. > > > 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(), [Zhenbin Xu] This issue is commonly solved by strict coding guideline. It is the integrator's job to ensure state integrity. Developers should not be handing begin/commit etc. inside their component. 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. > [Zhenbin Xu] The UA should make sure transaction is rolled back in case of exception. Alternatively, it would seem fitting to have a rollback() API here try { localStorage.begin(); .... localStorage.commit(); } catch { localStorage.rollback(); } > 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 [Zhenbin Xu] Not easy for script recover from exception. Either some garbage are left in localStorage, or a more complex scheme is needed. > } > 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; [Zhenbin Xu] If the id is persisting on disk and ever increasing, there is a danger of overflow. So a more elaborated scheme may be needed here. > 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 [Zhenbin Xu] It is difficult to do any meaningful error handling inside an utility function since it lacks context. Better leave it to the callers who have more information about the situation. > } > 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()); > }); > [Zhenbin Xu] How would application retrieve particular item later given that it has no knowledge of what block id is? If a wrapper function is needed all the time, why not make it as part of the platform? > 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 18:13:12 UTC