window.onload starts before indexed db statements - javascript

Good afternoon all,
My issue is javascript related, I have made one function called checkflights, a series of statements to open an indexeddb database and one window.onload that triggers checkflights.
It seems that the window.onload triggers before the open database statements and therefor the checkflights function does not run properly as the db is considered null.
Any solution? code here below. Thank you in advance for your support.
var db = null
const request = indexedDB.open('MyDataBase', '1')
//on upgrade needed
request.onupgradeneeded = e => {
var db = e.target.result
/* note = {
title: "note1",
text: "this is a note"
}*/
const myFlights = db.createObjectStore("my_flight", {
keyPath: "flightid"
})
}
request.onsuccess = e => {
var db = e.target.result
}
request.onerror = e => {
alert(`error: ${e.target.error} was found `)
}
window.onload = function () {
checkFlights()
}
function checkFlights() {
const tx = db.transaction("my_flight", "readonly");
// var objectStore = transaction.objectStore('my_flight');
const mesVols=tx.objectStore("my_flight")
var countRequest = mesVols.count();
countRequest.onsuccess = function() {
console.log(countRequest.result);
if(countRequest.result>0 && window.navigator.onLine){
sendFlights()
notify("Flights sent to server")
}
}
}

You are redeclaring db from the outer scope, by using var again.
When using var in a local scope, you are not affecting the variable from the outer scope and actually creating a new local db variable.
var db = null
const request = indexedDB.open('MyDataBase', '1');
//on upgrade needed
request.onupgradeneeded = e => {
db = e.target.result
/* note = {
title: "note1",
text: "this is a note"
}*/
const myFlights = db.createObjectStore("my_flight", {
keyPath: "flightid"
})
}
request.onsuccess = e => {
db = e.target.result
}
request.onerror = e => {
alert(`error: ${e.target.error} was found `)
}
window.onload = function () {
checkFlights()
}
function checkFlights() {
const tx = db.transaction("my_flight", "readonly");
// var objectStore = transaction.objectStore('my_flight');
const mesVols=tx.objectStore("my_flight")
var countRequest = mesVols.count();
countRequest.onsuccess = function() {
console.log(countRequest.result);
if(countRequest.result>0 && window.navigator.onLine){
sendFlights()
notify("Flights sent to server")
}
}
}
As suggested by #Kinglish in a comment above, you might need to wait for the request to be handled. IndexedDB does not return a promise but you could write an async/await wrapper yourself around the top part or consider using a library like https://github.com/jakearchibald/idb that will Promisify indexedDB.

Related

JS - How to retrieve variable after IndexedDB transaction.oncomplete() executes?

My problem is simple, but incredibly frustrating as I'm now on my second week of trying to figure this out and on the verge of giving up. I would like to retrieve my 'notesObject' variable outside my getAllNotes() function when after the transaction.oncomplete() listener executes.
(function() {
// check for IndexedDB support
if (!window.indexedDB) {
console.log(`Your browser doesn't support IndexedDB`);
return;
}
// open the CRM database with the version 1
let request = indexedDB.open('Notes', 1);
// create the Contacts object store and indexes
request.onupgradeneeded = (event) => {
let db = event.target.result;
// create the Notes object store ('table')
let store = db.createObjectStore('Notes', {
autoIncrement: true
});
// create an index on the sections property.
let index = store.createIndex('Sections', 'sections', {
unique: true
});
}
function insertData() {
let myDB = indexedDB.open('Notes');
myDB.onsuccess = (event) => {
// myDB.transaction('Notes', 'readwrite')
event.target.result.transaction('Notes', 'readwrite')
.objectStore('Notes')
.put({
sections: "New Note",
pages: "New page",
lastSelectedPage: ""
});
console.log("insert successful");
}
myDB.onerror = (event) => {
console.log('Error in NotesDB - insertData(): ' + event.target.errorCode);
}
myDB.oncomplete = (event) => {
myDB.close();
console.log('closed');
}
}
insertData()
function getAllNotes() {
let myDB = indexedDB.open('Notes');
let notesObject = [];
myDB.onsuccess = (event) => {
let dbObjectStore = event.target.result
.transaction("Notes", "readwrite").objectStore("Notes");
dbObjectStore.openCursor().onsuccess = (e) => {
let cursor = e.target.result;
if (cursor) {
let primaryKey = cursor.key;
let section = cursor.value.sections;
notesObject.push({
primaryKey,
section
})
cursor.continue();
}
}
dbObjectStore.transaction.onerror = (event) => {
console.log('Error in NotesDB - getAllData() tranaction: ' + event.target.errorCode);
}
dbObjectStore.transaction.oncomplete = (event) => {
return notesObject;
console.log(notesObject)
}
}
}
let notes = getAllNotes()
console.log("Getting Notes sucessful: " + notes)
})()
I've tried setting global variables, but nothing seems to work. I am a complete noob and honestly, I'm completely lost on how to retrieve the notesObject variable outside my getAllNotes() function. The results I get are 'undefined'. Any help would be greatly appreciated.
This is effectively a duplicate of Indexeddb: return value after openrequest.onsuccess
The operations getAllNotes() kicks off are asynchronous (they will run in the background and take time to complete), whereas your final console.log() call is run synchronously, immediately after getAllNotes(). The operations haven't completed at the time that is run, so there's nothing to log.
If you search SO for "indexeddb asynchronous" you'll find plenty of questions and answers about this topic.

Workaround for new Edge Indexeddb bug?

Microsoft released update kb4088776 in the past couple days, which has had a devastating effect on performance of indexedDb openCursor.
The simple fiddle here shows the problem. With the update, the "retrieval" time is 40 seconds or more. Prior to the update, it is around 1 second.
https://jsfiddle.net/L7q55ad6/23/
Relevant retrieval portion is here:
var _currentVer = 1;
function _openDatabase(fnSuccess) {
var _custDb = window.indexedDB.open("MyDatabase", _currentVer);
_custDb.onsuccess = function (event) {
var db = event.target.result;
fnSuccess(db);
}
_custDb.onerror = function (event) {
_custDb = null;
fnSuccess(null); // should use localData
}
_custDb.onupgradeneeded = function (event) {
var db = event.target.result;
var txn = event.target.transaction;
// Create an objectStore for this database
if (event.oldVersion < _currentVer) {
var customer = db.createObjectStore("customer", { keyPath: "guid" });
var index = customer.createIndex("by_id", "id", { unique: false });
}
};
}
function _retrieveCustomers(fn) {
_openDatabase(function (db) {
if (db == null)
{
alert("not supported");
return;
}
var customers = [];
var transaction = db.transaction("customer", "readonly");
var objectStore = transaction.objectStore("customer");
if (typeof objectStore.getAll === 'function') {
console.log("using getAll");
objectStore.getAll().onsuccess = function (event) {
fn(event.target.result);
};
}
else {
console.log("using openCursor");
objectStore.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
customers.push(cursor.value);
cursor.continue();
}
else {
fn(customers);
}
};
}
});
}
The time to create and add the customers is basically normal, only the retrieval is bad. Edge has never supported the getAll method and it still doesn't after the update.
The only workaround I can think of would be to use localStorage instead, but unfortunately our data set is too large to fit into the 10MB limit. It is actually faster now to retrieve from our servers and convert the text to javascript objects, defeating the main purpose of indexeddb.
I don't have Edge so I can't test this, but does it happen with get too, or just openCursor? If get still performs well, you could store an index (in your example, the list of primary keys; in your real app, maybe something more complicated) in localStorage, and then use that to call get on each one.

global variable is not accessible in firebase function

I declared a global array in index.js (firebase function). Once the code is deployed, this array is filled from firebase data.
I have two functions, in the first one (onTW) i made some changes to the array, and i'm just displaying it in the other function(onRemoveTW). The problem is I'm getting an empty array in the second function.
Here's my code.
var TWArray = [];
TWRef.once('value', function (snapshot) {
snapshot.forEach(function(childSnapshot) {
var name=childSnapshot.key;
var users = {};
var userNbr = 0;
TWRef.child(name).child('rm').once('value', function (snapshot2) {
snapshot2.forEach(function(childSnapshot2) {
userNbr++;
if(childSnapshot2.key=='a'){
users.a = childSnapshot2.val();
}
if(childSnapshot2.key=='b'){
users.b = childSnapshot2.val();
}
if(childSnapshot2.key=='c'){
users.c = childSnapshot2.val();
}
if(childSnapshot2.key=='d'){
users.d = childSnapshot2.val();
}
})
TWArray.push({
rmName:name,
users:users,
userNbr:userNbr
});
})
})
})
exports.onTW = functions.database
.ref('/Orders/TW/{requestId}')
.onWrite(event => {
const userKey = event.data.key;
const post = event.data.val();
if (post != null) {
var users={};
users.a=userKey;
TWArray.push({
rmName:userKey,
users:users,
userNbr:1
});
console.log(TWArray);
console.log("TWArray.length : "+TWArray.length);
}
});
exports.onRemoveTW = functions.database
.ref('/Orders/RemoveTW/{requestId}')
.onWrite(event => {
const userKey = event.data.key;
const post = event.data.val();
if (post != null) {
console.log("TWArray.length : "+TWArray.length);
}
})
Thanks in advance!
You cannot share data between functions by writing to global variables when using firebase-functions, because they intended to be stateless. As such, this functionality is not supported.
What you can do is write your data to firebase-database instead.

IndexedDB via Lawnchair gets larger with every save

I am using Lawnchair.js on a mobile app I am building at work targeting iOS, Android, and Windows phones. My question is I have a relatively simple function(see below), that reads data from an object and saves it in the indexeddb database. It's about 4MB of data and on the first go round when I inspect in Internet explorer(via internet options), I can see the database is about 7MB. If I reload the page and re-run the same function with the same data, it increases to 14MB and then 20MB. Im using the same keys so my understanding is that this should just update the record but it's almost as if it's just inserting all new records every time. I have also had similar behavior using Lawnchair on mobile safari using websql adapter. Has anyone seen this before or have any suggestions as to why this might be ??.
The following code is from a function I am using to populate the database.
populateDatabase: function(database,callback) {
'use strict';
var key;
try {
for(key in MasterData){
if(MasterData.hasOwnProperty(key)){
var itemInfo = DataConfig.checkForDataUpdates[DataConfig.keyMap[key]];
database.save({key:itemInfo["name"],hash:itemInfo["version"],url:itemInfo["url"],data:MasterData[key]});
}
}
callback(true);
} catch(e){
callback(false);
}
}
MasterData is the large data file and itemInfo contains the key name, a hash that is later used to check an api for updates, and the relative url of where to update from. After I create the database I pass it into this function and then pass back true if the inserts are successful and false otherwise.
As previously mentioned, I have seen similar issues in iOS where calling database.save() was allocating a lot of memory but not releasing it and eventually causing a crash if it populated the database and then tried to update some records. Removing Lawnchair from the equation has kept it from crashing but it is still allocating a lot of memory when saving data. Not sure if this is normal for persistent storage on mobile devices, a bug in Lawnchair, or me being a noob and doing something terribly wrong but I could use some pointers on this as well as why indexeddb just keeps getting larger and larger on every save (at least during initial testing in IE10)??
EDIT: Source Code for indexed-db adapter is here:
https://github.com/brianleroux/lawnchair/blob/master/src/adapters/indexed-db.js
and here is the code for the save function I am using:
save:function(obj, callback) {
var self = this;
if(!this.store) {
this.waiting.push(function() {
this.save(obj, callback);
});
return;
}
var objs = (this.isArray(obj) ? obj : [obj]).map(function(o){if(!o.key) { o.key = self.uuid()} return o})
var win = function (e) {
if (callback) { self.lambda(callback).call(self, self.isArray(obj) ? objs : objs[0] ) }
};
var trans = this.db.transaction(this.record, READ_WRITE);
var store = trans.objectStore(this.record);
for (var i = 0; i < objs.length; i++) {
var o = objs[i];
store.put(o, o.key);
}
store.transaction.oncomplete = win;
store.transaction.onabort = fail;
return this;
},
When Creating a new instance, Lawnchair uses the init function from the indexed-db adapter which is the following.
init:function(options, callback) {
this.idb = getIDB();
this.waiting = [];
this.useAutoIncrement = useAutoIncrement();
var request = this.idb.open(this.name, STORE_VERSION);
var self = this;
var cb = self.fn(self.name, callback);
if (cb && typeof cb != 'function') throw 'callback not valid';
var win = function() {
// manually clean up event handlers on request; this helps on chrome
request.onupgradeneeded = request.onsuccess = request.error = null;
if(cb) return cb.call(self, self);
};
var upgrade = function(from, to) {
// don't try to migrate dbs, just recreate
try {
self.db.deleteObjectStore('teststore'); // old adapter
} catch (e1) { /* ignore */ }
try {
self.db.deleteObjectStore(self.record);
} catch (e2) { /* ignore */ }
// ok, create object store.
var params = {};
if (self.useAutoIncrement) { params.autoIncrement = true; }
self.db.createObjectStore(self.record, params);
self.store = true;
};
request.onupgradeneeded = function(event) {
self.db = request.result;
self.transaction = request.transaction;
upgrade(event.oldVersion, event.newVersion);
// will end up in onsuccess callback
};
request.onsuccess = function(event) {
self.db = event.target.result;
if(self.db.version != (''+STORE_VERSION)) {
// DEPRECATED API: modern implementations will fire the
// upgradeneeded event instead.
var oldVersion = self.db.version;
var setVrequest = self.db.setVersion(''+STORE_VERSION);
// onsuccess is the only place we can create Object Stores
setVrequest.onsuccess = function(event) {
var transaction = setVrequest.result;
setVrequest.onsuccess = setVrequest.onerror = null;
// can't upgrade w/o versionchange transaction.
upgrade(oldVersion, STORE_VERSION);
transaction.oncomplete = function() {
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
};
};
setVrequest.onerror = function(e) {
setVrequest.onsuccess = setVrequest.onerror = null;
console.error("Failed to create objectstore " + e);
fail(e);
};
} else {
self.store = true;
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
}
}
request.onerror = function(ev) {
if (request.errorCode === getIDBDatabaseException().VERSION_ERR) {
// xxx blow it away
self.idb.deleteDatabase(self.name);
// try it again.
return self.init(options, callback);
}
console.error('Failed to open database');
};
},
I think you keep adding data instead of updating the present data.
Can you provide some more information about the configuration of the store. Are you using an inline or external key? If it's an internal what is the keypath.

Functions are undefined

I am trying to create a data management application, but instead of a Windows-based solution or using WebSQL, i am using IndexedDB. I am pretty new to it but I believe I have covered the basis in this draft of code.
Anyway, my problem is, anytime I run the code, my openDB() function and the addeventListener() function both run and show on the console log at runtime but all other functions are said to be undefined when I try to run the code. What could the problem be?
In the HTML file, the jQuery script file is referenced.
(function () {
var DB_NAME = 'shodex';
var DB_VERSION = 1;
var DB_STORE_NAME = 'visitors';
var db;
var current_view_pub_key;
//opens the IndexedDB database
function openDb() {
console.log("open Database......");
var req = indexedDB.open(DB_NAME, DB_VERSION);
req.onsuccess = function (evt) {
db = this.result;
console.log("Database Opened");
};
req.onerror = function (evt) {
console.error("openDb:", evt.target.errorCode);
};
req.onupgradeneeded = function (evt) {
console.log("Event fired when DB is needed to be upgraded");
var store = evt.currentTarget.result.createObjectStore(
DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
store.createIndex('name', 'name', { unique: false });
store.createIndex('date', 'date', { unique: false });
store.createIndex('whom_to_see', 'whom_to_see', { unique: false });
store.createIndex('arrival_time', 'arrival_time', { unique: false });
store.createIndex('reason', 'reason', { unique: false });
store.createIndex('departure_time', 'departure_time', { unique: false });
};
}
//used to create a transaction
function getObjectStore(store_name, mode) {
var tx = db.transaction(store_name, mode);
return tx.objectStore(store_name);
}
//adds a Visitor to the IndexedDB
function addVisitor(name, date, to_see, arrival_time, reason, departure_time) {
console.log("Adding the following data to IndexedDB: ", arguments);
var obj = { name: name, date: date, whom_to_see: to_see, arrival_time: arrival_time, reason: reason, departure_time: departure_time };
if(typeof blob != undefined)
{
obj.blob = blob;
}
var store = getObjectStore(DB_STORE_NAME, 'readwrite');
var req;
try
{
req = store.add(obj);
}
catch(e)
{
if(e.name == 'DataCloneError')
displayActionFailure("This engine does not know how to clone a Blob, use Firefox!");
throw(e);
}
req.onsuccess = function (evt) {
console.log("Insertion into DB was successful. You can heave a huge sigh of relief!");
displayActionSuccess();
};
req.onerror = function () {
console.error("Insertion into DB failed!");
displayActionFailure(this.error);
};
}
function displayActionSuccess() {
alert("Whatever the heck you were doing was successful. Congrats!");
}
function displayActionFailure() {
alert("Oh Oh! System Failure! System Failure!");
}
// listens for the submit button event
function addEventListeners() {
console.log("Event Listeners");
$('#addVisitor').click(function(evt) {
console.log("Add Visitors Submit button");
var name = document.getElementsByName("txtName").value;
var date = document.getElementsByName("txtDate").value;
var whom_to_see = document.getElementsByName("txtToSee").value;
var time_of_arrival = document.getElementsByName("txtArrivalTime").value;
var reason_for_visit = document.getElementsByName("txtReason").value;
var time_of_departure = document.getElementsByName("timeOfDep");
addVisitor(name, date, whom_to_see, time_of_arrival, reason_for_visit, time_of_departure);
});
}
//makes the database open at runtime
openDb();
addEventListeners();
})
();
syntax error - you need a closing round bracket at end.
i.e add
);
I think the problem is the fact that when your anonymous function is run the browser doesn't not know about the '#addVisitor' element, so the click event handler for that element is not created.
You should put your code inside "$(function() {" or "$(document).ready(function() {":
$(function() {
(function () {
var DB_NAME = 'shodex';
var DB_VERSION = 1;
...
Instead of referencing the javascript code like before...
I removed all other functions from the javascript file leaving the openDB() function and placed them in the main html file. it worked automatically.

Categories

Resources