Search code examples
javascriptindexeddb

Use of indexedDB returns 'undefined'


I'm trying to use indexedDB.

Some parts of my code works.

In the following example, the first function adds server in my DB, however in Chrome debug console there is an undefined message not related to any line. The server is already added though.

The second function puts records in an array, there is also an undefined message not related to any line.

If I do a console.log(servers); just before return servers; I can see the array content, however if I call the function somewhere else in my code, the returned object is undefined.

var dbName = 'myDBname',
dbServersStoreName = 'servers',
dbVersion = 1,
openDBforCreation = indexedDB.open(dbName, dbVersion);

openDBforCreation.onupgradeneeded = function(e) {
 var db = e.target.result;
 var objStore = db.createObjectStore(dbServersStoreName, { keyPath: "alias" 
});
var index = objStore.createIndex("serversAlias", ["alias"]);
};

function addServerInDB(serverAlias,serverAddress,user,pwd){
 var myDB = indexedDB.open(dbName, dbVersion);

 myDB.onerror = function() {
  var notification = document.querySelector('.mdl-js-snackbar');
   notification.MaterialSnackbar.showSnackbar(
    {message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
 var db = e.target.result,
     request = db.transaction([dbServersStoreName], 
               "readwrite").objectStore("servers")
                .put({alias:''+serverAlias+'', 
                address:''+serverAddress+'', login:''+user+'', 
                passwd:''+pwd+''});

     request.onsuccess = function(){
      var notification = document.querySelector('.mdl-js-snackbar');
       notification.MaterialSnackbar.showSnackbar(
        {message: 'Server added'});
     }
 }
};

function listServersInDB(){
 var myDB= indexedDB.open(dbName, dbVersion);

myDB.onerror = function() {
    var notification = document.querySelector('.mdl-js-snackbar');
    notification.MaterialSnackbar.showSnackbar(
        {message: 'Error while trying to access internal database'});

}
myDB.onsuccess = function(e) {
    var servers = new Array(),
        db = e.target.result,
        request = db.transaction(["servers"], "readwrite")
                    .objectStore("servers")
                    .openCursor();

    request.onsuccess = function(e){
        var cursor = e.target.result;
        if(cursor){
            servers.push(cursor.value);
            cursor.continue();
        }
        return servers;
    }
}
};

I do not understand where this undefined comes from and if that is why the listServersInDB() function doesn't work.


Solution

  • You need to learn more about how to write asynchronous Javascript. There are too many errors in your code to even begin reasoning about the problem.

    Briefly, don't do this:

    function open() {
      var openDatabaseRequest = ...;
    }
    
    openDatabaseRequest.foo = ...;
    

    Instead, do this:

    function open() {
      var openDatabaseRequest = ...;
      openDatabaseRequest.foo = ...;
    }
    

    Next, you don't need to try and open the same database multiple times. Why are you calling indexedDB.open twice? You can open a database to both install it and to start using it immediately. All using the same connection.

    Next, I'd advise you don't name the database open request as 'myDB'. This is misleading. This is an IDBRequest object, and more specifically, an IDBOpenRequest object. A request isn't a database.

    Next, you cannot return the servers array from the request.onsuccess at the end. For one this returns to nowhere and might be source of undefined. Two this returns every single time the cursor is advanced, so it makes no sense at all to return return servers multiple times. Three is that this returns too early, because it cannot return until all servers enumerated. To properly return you need to wait until all servers listed. This means using an asynchronous code pattern. For example, here is how you would do it with a callback:

    function listServers(db, callbackFunction) {
      var servers = [];
      var tx = db.transaction(...);
      var store = tx.objectStore(...);
      var request = store.openCursor();
    
      request.onsuccess = function() {
        var cursor = request.result;
        if(cursor) {
          servers.push(cursor.value);
          cursor.continue();
        }
      };
    
      tx.oncomplete = function() {
        callbackFunction(servers);
      };
    
      return 'Requested servers to be loaded ... eventually callback will happen';
    }
    
    
    function connectAndList() {
      var request = indexedDB.open(...);
      request.onsuccess = function() {
        var db = request.result;
        listServers(db, onServersListed);
      };
    }
    
    function onServersListed(servers) {
      console.log('Loaded servers array from db:', servers);
    }
    

    When you call a function that does not return a value, it returns undefined. All functions in JavaScript return undefined unless you explicitly return something else.

    When you call a function from the devtools console, and that function returns undefined, then the console prints out '-> undefined'. This is an ordinary aspect of using the console.

    If you want to get a function that returns the list of servers as an array, well, you cannot. The only way to do that in a pretend sort of way, is to use an 'async' function, together with promises.

    async function getServers() {
      var db = await new Promise(resolve => {
        var request = indexedDB.open(...);
        request.onsuccess = () => resolve(request.result);
      });
    
      var servers = await new Promise(resolve => {
        var tx = db.transaction(...);
        var request = tx.objectStore(...).getAll();
        request.onsuccess = () => resolve(request.result);
      });
      return servers;
    }
    

    One more edit, if you want to call this from the console, use await getServers();. If you do not use the top-level await in console, then you will get the typical return value of async function which is a Promise object. To turn a promise into its return value you must await it.