Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does onsuccess sometimes get called before onupgradeneeded when connecting to indexedDB?

I am having trouble with IndexedDB. On Firefox 18, when I create a new database, the onsuccess method is called at the same time has onupgradeneeded. On Chrome 24 (this is the behavior I'd like to get), the onsuccess method is only called after the onupgradeneeded method has completed.

According to the MDN information on IndexedDB, I was under the impression that when the onsuccess method was called, it was safe to work with the database but this make it seems like it is not in Firefox.

(function(){
  app = {};

  // These will hold the data for each store.
  app.objectstores = [
    { name: 'UNIVERSITIES',
      keyPath: 'UID',
      autoIncrement: false,
      data_source: 'http://mysites.dev/nddery.ca_www/larelance/data/universite.json' },
  ];

  // Some information pertaining to the DB.
  app.indexedDB    = {};
  app.indexedDB.db = null
  app.DB_NAME      = 'testdb';
  app.DB_VERSION   = 1;

  /**
   * Attempt to open the database.
   * If the version has changed, deleted known object stores and re-create them.
   * We'll add the data later.
   *
   */
  app.indexedDB.open = function() {
    // Everything is done through requests and transactions.
    var request = window.indexedDB.open( app.DB_NAME, app.DB_VERSION );

    // We can only create Object stores in a onupgradeneeded transaction.
    request.onupgradeneeded = function( e ) {
      app.indexedDB.db = e.target.result;
      var db = app.indexedDB.db;

      // Delete all object stores not to create confusion and re-create them.
      app.objectstores.forEach( function( o ) {
        if ( db.objectStoreNames.contains( o.name ) )
          db.deleteObjectStore( o.name );

        var store = db.createObjectStore(
          o.name,
          { keyPath: o.keyPath, autoIncrement: o.autoIncrement }
        );

        app.indexedDB.addDataFromUrl( o.name, o.data_source );
      });
    }; // end request.onupgradeneeded()

    // This method is called before the "onupgradeneeded" has finished..??
    request.onsuccess = function( e ) {
      app.indexedDB.db = e.target.result;
      app.ui.updateStatusBar( 'Database initialized...' );

      // ***
      // Would like to query the database here but in Firefox the data has not
      // always been added at this point... Works in Chrome.
      //
    }; // end request.onsuccess()

    request.onerror = app.indexedDB.onerror;
  }; // end app.indexedDB.open()


  app.indexedDB.addDataFromUrl = function( store, url ) {
    var xhr = new XMLHttpRequest();
    xhr.open( 'GET', url, true );
    xhr.onload = function( event ) {
      if( xhr.status == 200 ) {
        console.log('*** XHR successful');
        // I would be adding the JSON data to the database object stores here.
      }
      else{
        console.error("addDataFromUrl error:", xhr.responseText, xhr.status);
      }
    };
    xhr.send();
  }; // end app.indexedDB.addDataFromUrl()
})();

Thanks!

like image 624
nddery Avatar asked Feb 16 '13 00:02

nddery


2 Answers

One of the things you are probably suffering with is the auto-commit functionality in the indexeddb. If an transaction becomes inactive for a short timespan, it will commit the transaction and close it.

In your case you are calling an async method to fill up the data, and that is why the transaction probably becomes inactive. if you add a console.write after app.indexedDB.addDataFromUrl( o.name, o.data_source ); you will see it will get called before your data is retrieved, causing the commit of the transaction. That is why the data isn't present when the success callback is called. It is possible that the timeout for transactions is higher in chrome than in Firefox. It isn't described in the spec so it can vary by vendor.

btw, if you want to add data in your ajax call, you will have to pass the object store as a parameter as well.

like image 159
Kristof Degrave Avatar answered Sep 22 '22 01:09

Kristof Degrave


do what you have to do inside if( xhr.status == 200 ) {} call a transaction from there, put the data to the objectStore and what more you need

edit:

I do it like this, works for me, the oncomplete function only gets called once all the data is inserted and the objectStore is ready for use:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://...", true);
xhr.addEventListener("load", function(){
    if(xhr.status === 200){
        console.info("Data received with success");
        var data = JSON.parse(xhr.responseText);
        var transaction = db.transaction([STORe],'readwrite');
        var objstore = transaction.objectStore(STORE);
        for(i = 0; i < data.length; i++){
            objstore.put(data[i]);
        };
        transaction.oncomplete = function(event){
            //do what you need here
            console.info("Inserted data: " + data.length);
        };
        transaction.onerror = function(event){              
        };
        transaction.onabort = function(event){
        };
        transaction.ontimeout = function(event){
        };
        transaction.onblocked = function(event){
        };                      
    };
}, false);
xhr.send();
like image 29
Ruben Teixeira Avatar answered Sep 22 '22 01:09

Ruben Teixeira