This weekend I had the pleasure of dealing with asynchronous api IndexedDB.
The
mozilla example parse field seemed to me that the code was not as good as it could have been.
And I decided to develop my
psionic skills of working with IndexedDB to write a wrapper to its asynchronous api.
')
To be honest, I am not a fan of writing articles, because I have always been too lazy to read words, I like to read the code, therefore in my articles there are usually a lot of it.
If you like the code too, then I put it on
github
In fact, this article describes what is written in the
examples of using a wrapper , maybe a little more.
By the way, about the code, many people probably thought that you would have looked at the code that I have just or recently read, written here ...
Yes, sometimes they slip even that garbage. I treat this code philosophically and with humor. I sincerely hope that I will not laugh you, dear reader.
What I did not like in the current api
The fact that developers of the html5 standard / browsers now offer us have several drawbacks:
- You have to write extra code to implement call chains;
- Write your event handlers for logical operations: creating a repository and its contents, updating / creating an entity, completing work with a cursor;
- Support multiple browsers. Now, many browsers have not implemented certain features of IndexedDB, for example, count and onupgradeneeded are not in the webkit;
- Redundancy - remember the many functions that do about the same thing. For example: IDBObjectStore.get, IDBObjectStore.openCursor, IDBIndex.openKeyCursor, IDBIndex.openCursor
Take for example the
creation of a repository . I personally think that such a code is cumbersome. I would have liked it better if the code looked like this:
- var init = function ( event ) {
- if ( db. containts ( 'customers' ) )
- return ;
- db. createStore ( 'customers' , { keyPath : 'ssn' } )
- . createIndex ( 'name' , { unique : false } )
- . createIndex ( 'age' , { unique : false } )
- . createIndex ( 'email' , { unique : true } )
- . complete ( function ( event ) {
- console. info ( 'store customers created' , event ) ;
- } )
- . add ( customerData )
- . error ( errorHandler )
- . success ( function ( event ) {
- console. info ( 'add success' , event ) ;
- } ) ;
- } ;
This code is slightly more functional than the code from the example, for example, events are recorded for all operations, such as:
- there is an additional processing of the completion of creation and filling - complete
- error processing
- successful add
Also, because The work is conducted not in the application that was previously installed to the client, but from the web. This means that the page could be loaded for the first time, I would like to add some events to the context of working with the database, for example:
- var db = new inDB ( { name : 'testDatabase' , version : 42 } )
- . error ( errorHandler )
- // before ready
- . init ( init )
- . versionChange ( function ( event ) {
- console. log ( 'version changed, timeStamp:' , event. timeStamp , ', new version:' , this . version ) ;
- } )
- // WebKit, as of 2012-02-22, does not yet implement this.
- . upgradeNeeded ( function ( event ) {
- console. log ( 'onupgradeneeded, newVersion:' , event. newVersion , ', oldVersion:' , event. oldVersion , ', timeStamp:' , event. timeStamp , event ) ;
- init. call ( this , event ) ;
- } )
- . ready ( function ( event ) {
- // ...
- } ) ;
Those. learn about the version change, initialize the storage and fill them with data, and then start working when the storage is ready
, asnhronka, edr compote .
If I did not convince you, then further reading is meaningless.
My way
As for me, these considerations were enough for me to continue. Because what I saw
later in the mozilla example , I did not like it even more. Those. Many thanks to the guys for the example, but I do not want to live like this). I would have liked it a lot more if it looked like this:
- db. openStore ( 'customers' )
- . get ( '444-44-4444' )
- . error ( errorHandler )
- . success ( function ( event ) {
- console. log ( this . result ) ;
- } ) ;
And to choose from another index like this:
- db. openStore ( 'customers' )
- . get ( 'name' , 'artur' )
- // ...
To select data from the cursor, I want to have the execution context and separate blocks for the start of the operation, intermediate values and the end, something like this:
- db. openStore ( 'customers' )
- . get ( function ( query ) {
- return query
- . bound ( 'age' , 30 , 60 , true , true ) ; // all age 30> x && <60
- // only one predicate by design indexddb, please use where after get
- } )
- . where ( function ( item ) {
- return item . email . substr ( - 8 ) . toLowerCase ( ) ! = 'home.org' ;
- } )
- . error ( errorHandler )
- . start ( function ( context ) {
- context. result = [ ] ;
- console. time ( 'get all by IDBKeyRange' ) ;
- } )
- . ended ( function ( context ) {
- console. timeEnd ( 'get all by IDBKeyRange' , context. result ) ;
- } )
- . success ( function ( event , context ) {
- var customer = this . result . value ;
- console. info ( 'getted by IDBKeyRange' , customer. ssn , customer , event , this ) ;
- if ( customer. ssn == '111-11-1111' ) {
- customer age = 6 ;
- // only for cursor
- context. update ( customer ) ;
- }
- context. result . push ( customer ) ;
- } ) ;
I realized my vision of how asynchronous access to a database with such an architecture would look like in
indb.js.
There is still a lot of tasty things, for example, you can register events about changes / creation of storage objects, it is an order of magnitude easier to delete a database or storage.
PS: For those who run the example code in FF, now there is a curious bug in the implementation of the IndexedDB engine. If you change customer.age = 6; at ++ customer.age; then after re-creating the base, you will get Maria at 60, not 41. It is also curious that if you restart the script before the cleanup is done by timeout in 5 seconds, everything will be Ok.
UPD 1: To run in FF, it will be necessary to start not from the file system, but on any host, localhost will do).
UPD 2: for the server part you can look in the direction of
http://tamejs.org/ , thanks to Andrei Kozlov for the link)