Estoy tratando de usar indexedDB.

Algunas partes de mi código funcionan.

En el siguiente ejemplo, la primera función agrega servidor en mi base de datos, sin embargo, en la consola de depuración de Chrome hay un mensaje undefined que no está relacionado con ninguna línea. Sin embargo, el servidor ya está agregado.

La segunda función coloca los registros en una matriz, también hay un mensaje undefined que no está relacionado con ninguna línea.

Si hago un console.log(servers); justo antes de return servers; puedo ver el contenido de la matriz, sin embargo, si llamo a la función en algún otro lugar de mi código, el objeto devuelto es 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;
    }
}
};

No entiendo de dónde viene este undefined y si es por eso que la función listServersInDB() no funciona.

0
Nico 19 feb. 2018 a las 14:17

2 respuestas

La mejor respuesta

Necesita aprender más sobre cómo escribir JavaScript asincrónico. Hay demasiados errores en su código para siquiera comenzar a razonar sobre el problema.

Brevemente, no hagas esto:

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

openDatabaseRequest.foo = ...;

En cambio, haz esto:

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

A continuación, no es necesario que intente abrir la misma base de datos varias veces. ¿Por qué llamas a indexedDB.open dos veces? Puede abrir una base de datos para instalarla y comenzar a usarla de inmediato. Todos usando la misma conexión.

A continuación, le aconsejo que no nombre la solicitud de apertura de la base de datos como 'myDB'. Esto es engañoso. Este es un objeto IDBRequest y, más específicamente, un objeto IDBOpenRequest. Una solicitud no es una base de datos.

A continuación, no puede devolver la matriz de servidores de request.onsuccess al final. Por un lado, esto vuelve a la nada y podría ser fuente de indefinido. Dos, esto devuelve cada vez que se avanza el cursor, por lo que no tiene ningún sentido devolver los servidores de retorno varias veces. La tercera es que esto regresa demasiado pronto, porque no puede regresar hasta que todos los servidores estén enumerados. Para regresar correctamente, debe esperar hasta que se enumeren todos los servidores. Esto significa utilizar un patrón de código asincrónico. Por ejemplo, así es como lo haría con una devolución de llamada:

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

Cuando llama a una función que no devuelve un valor, devuelve undefined. Todas las funciones en JavaScript devuelven undefined a menos que devuelva explícitamente algo más.

Cuando llamas a una función desde la consola devtools, y esa función devuelve undefined, la consola imprime '-> undefined'. Este es un aspecto normal del uso de la consola.

Si desea obtener una función que devuelva la lista de servidores como una matriz, bueno, no puede. La única forma de hacer eso de una manera fingida, es usar una función 'asincrónica', junto con promesas.

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

Una edición más, si desea llamar a esto desde la consola, use await getServers();. Si no usa la espera de nivel superior en la consola, obtendrá el valor de retorno típico de la función asíncrona, que es un objeto Promise. Para convertir una promesa en su valor de retorno, debe esperarla.

3
Josh 19 feb. 2018 a las 18:39

Explicación clara y útil, gracias.

Abro la base de datos varias veces porque la primera vez es para verificar si DB necesita una actualización y hacer algo si es necesario. Agregaré 'db.close ()' en cada función.

Luego, probé tu ejemplo y el resultado es el mismo: console.log('Loaded servers array from db:', servers); funciona pero return servers; no funciona.

Y en la consola ya hay un undefined sin línea relacionada:

Captura

0
Nico 19 feb. 2018 a las 17:16