javascript - Prečo sa niekedy onsuccess zavolá pred onupgradeneeded pri pripájaní sa k indexedDB?


Mám problémy s IndexedDB. Vo Firefoxe 18, keď vytvorím novú databázu, metóda onsuccess sa volá súčasne s onupgradeneeded. Na Chrome 24 (toto je správanie, ktoré by som chcel dosiahnuť) sa metóda onsuccess volá až po dokončení metódy onupgradeneeded.

Podľa informácií na MDN o IndexedDB som mal dojem, že keď sa zavolá metóda onsuccess, je bezpečné pracovať s databázou, ale toto to vyzerá, že vo Firefoxe to tak nie je.

(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()
})();

Ďakujem!

Odpoveď č. 1

Jednou z vecí, s ktorými pravdepodobne trpíte, je funkcia automatického potvrdenia (auto-commit) v IndexedDB. Ak sa transakcia stane neaktívnou na krátky čas, potvrdí sa transakcia a uzavrie sa.

Vo vašom prípade voláte asynchrónnu metódu na naplnenie dát, a preto sa transakcia pravdepodobne stáva neaktívnou. Ak po app.indexedDB.addDataFromUrl(o.name, o.data_source); pridáte console.write, uvidíte, že sa zavolá skôr, než sa vaše dáta načítajú, čo spôsobuje potvrdenie transakcie. Preto dáta nie sú prítomné, keď je zavolaný callback pre úspech. Je možné, že časový limit pre transakcie je v Chrome vyšší než vo Firefoxe. Nie je to popísané v špecifikácii, takže sa to môže líšiť podľa dodávateľa.

Mimochodom, ak chceš pridať dáta do svojho ajax volania, budeš musieť tiež odovzdať objekt store ako parameter.

Odpoveď č. 2

urob to, čo musíš urobiť vo vnútri if( xhr.status == 200 ) {}, zavolaj z toho transakciu, vlož dáta do objectStore a čo viac potrebuješ

upraviť :

Robím to takto, mne to funguje, funkcia oncomplete sa zavolá až po vložení všetkých údajov a keď je objectStore pripravený na použitie:

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();