jQuery.Deferred() is not working properly - javascript

I'm trying to implement indexedDB. For that I need to use $.Deferred to get the status of database creation. But the problem is, Differed is not working as expected.
Here is the fiddle, you can find the process in the console.
And here is the code:
$(function($) {
var table = 'price';
$.when(dbConnection('cw', table)).done(function(db) {
console.log(db);
var data = [];
dbInsert(db, data, table);
});
function dbConnection(dbname, table) {
var dfd = $.Deferred();
var request = indexedDB.open(dbname);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = request.result;
var store = db.createObjectStore(table, {
keyPath: "id"
});
var styleCode = store.createIndex("style_code", "style_code");
var colorCode = store.createIndex("color_code", "color_code");
var size = store.createIndex("size", "size");
var price1 = store.createIndex("price1", "price1");
};
request.onsuccess = function() {
db = request.result;
console.log(request.result);
dfd.resolve(db);
return dfd.promise();
};
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
return dfd.promise();
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
return dfd.promise();
};
}
function dbInsert(db, data, table) {
var tx = db.transaction(table, "readwrite");
var store = tx.objectStore(table);
$.each(data, function(i, rows) {
var style = rows['style-code'],
color = rows['color-code'],
size = rows['size'],
price = rows['price1'];
store.put({
id: i,
style_code: style,
color_code: color,
size: size,
price1: price
});
});
tx.oncomplete = function() {
console.log('Data inserted successfully.');
};
}
})(jQuery);
Whether I'm doing anything wrong? or am I missing anything in this code?. Can anyone tell me what is wrong in this code.

You are expecting dbConnection to return a promise, but do not return anything from that function. Return the promise immediately (last line below) and not inside all the callbacks:
function dbConnection(dbname, table) {
var dfd = $.Deferred();
var request = indexedDB.open(dbname);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = request.result;
var store = db.createObjectStore(table, {
keyPath: "id"
});
var styleCode = store.createIndex("style_code", "style_code");
var colorCode = store.createIndex("color_code", "color_code");
var size = store.createIndex("size", "size");
var price1 = store.createIndex("price1", "price1");
};
request.onsuccess = function() {
db = request.result;
console.log(request.result);
dfd.resolve(db);
};
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
};
return dfd.promise();
}
Updated JSFiddle: http://jsfiddle.net/TrueBlueAussie/9kjcm49b/2/
Your various callbacks simply resolve or reject the deferred. The readonly promise returned earlier then triggers the next operation.
Note: You should probably use reject for the two error cases (unless you actually want to proceed with a null db value). e.g.
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.reject("Error occurred");
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.reject("Operation aborted");
};
and use like this:
$.when(dbConnection('cw', table)).done(function(db) {
console.log(db);
var data = [];
dbInsert(db, data, table);
}).fail(function(message){
alert(message);
});

Related

When using IndexedDB, how can I delete multiple records using an index that is not the key?

I have code to create an indexedDB here:
function create_db() {
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.msIndexedDB;
var request = indexedDB.open(“photos”, 2);
request.onupgradeneeded = function(event) {
var db = event.target.result;
// Create photo db
var photo_store = db.createObjectStore("photos", {keyPath: "photo_id"});
var photo_id_index = photo_store.createIndex("by_photo_id", "photo_id", {unique: true});
var dest_id_index = photo_store.createIndex("by_destination_id", "destination_id");
console.log(“store created”);
};
request.onsuccess = function(event) {
console.log(“store opened”);
};
request.onerror = function(event) {
console.log("error: " + event);
};
}
My code to remove entries:
function remove_photos = function (destination_id, db) {
var transaction = db.transaction("photos", "readwrite");
var store = transaction.objectStore("photos");
var index = store.index("by_destination_id");
var request = index.openCursor(IDBKeyRange.only(destination_id));
request.onsuccess = function() {
var cursor = request.result;
if (cursor) {
cursor.delete();
cursor.continue();
}
};
}
How can I delete records using the by_destination_id index so that I can delete all records with a given destination_id, which is an integer?
Thanks for any help.
I found the solution to my issue, the IDBKeyRange.only function doesn't like integers it needs to be a string, so substituting this line in:
var request = index.openCursor(IDBKeyRange.only(destination_id.toString()));
Make the code work.

Returning data from a class

I have a file drop class. User drop images on (as many as they wish) and these are then uploaded.
I call the class via from my main class:
this.fileDrop = new lx.FileDrop();
Here's the class:
(function(){
"use strict";
var FileDrop = function() {
this.init();
};
p.init = function() {
this._initEvents();
};
p._initEvents = function() {
$(window).on('drop', this.onDrop.bind(this)).on('dragover', this.onDragOver);
};
p.onDrop = function(e) {
e.preventDefault();
var self = this;
var files = e.originalEvent.dataTransfer.files;
$.each(files, function(index, file){
self.readFile(file).done(function(data) {
//how to return the data?
});
});
};
p.onDragOver = function(e) {
e.preventDefault();
};
p.readFile = function(file) {
var fileReader = new FileReader();
var deferred = $.Deferred();
fileReader.onload = function(event) {
deferred.resolve(event.target.result);
};
fileReader.onerror = function() {
deferred.reject(this);
};
fileReader.readAsDataURL(file);
return deferred.promise();
};
lx.FileDrop = FileDrop;
}(window));
My question concerns returning the image data to the main class. How can I return it? How can I store it - I wish to store everything that is returned into an array in the main class. How would this work when uploading multiple images. Would some sort of deferred work?
How about something like this:
var dfd = $.Deferred(),
images = [];
$.each(files, function(index, file){
self.readFile(file).done(function(data) {
dfd.progress(data);
images.push(data);
if(files.length === ++index)
dfd.resolve(images);
}).fail(dfd.reject);
});
Handle the deferred object where ever you like:
dfd.progress(function(file){
console.log('file successfully uploaded', file);
}).done(function(images){
console.log('all files successfully uploaded', images);
}).fail(function(){
console.log('something went wrong while uploading an image');
});
Another example:
function FileDrop(){
this.uploadCount = 0;
this.images = [];
}
FileDrop.prototype.getImages = function(){
var dfd = $.Deferred(),
size = 3,
that = this;
for(var i = 0; i < size; i++){
this.getImage(i*500, function(image){
var dummyImage = $('<img/>');
// should be 'image' from the callback in your case
that.images.push(dummyImage);
dfd.notify(dummyImage);
if(that.uploadCount === size){
dfd.resolve(that.images);
}
});
}
return dfd.promise();
};
FileDrop.prototype.getImage = function(timeout, callback){
var that = this;
setTimeout(function(){
that.uploadCount++;
callback();
}, timeout);
};
var fd = new FileDrop();
fd.getImages().progress(function(image){
console.log('progress', image);
}).done(function(imageArray){
// preferred way:
//access the array when you know it's complete in the callback
console.log('done', imageArray);
});
setTimeout(function(){
// I think this is what you asked for, however, you must make an
// assumption when the images are completed, which is a bad idea
console.log(fd.images);
}, 2000);
http://jsfiddle.net/Nm5vK/2/

indexedDB blocked DOM exception after an insert loop

I'm doing few tests with indexedDB and I keep getting chrome blocked during an insert test.
Basically what I'm doing is a simple loop, 100000 times, inserting simple string in the db. It completes correctly but at the end the indexedDB from the inspector is not visible. Not even refreshing the page. If trying to reload the page and reopening the database I get a DOM exception. If closing chrome it hangs and I have to kill it.
Below the code:
var testIndexedDB = {
db : null,
request: null,
openDB : function(){
var self = this;
var request = this.request = indexedDB.open("web", 1);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
request.close();
var db = request.result;
var stories = db.createObjectStore("stories", {keyPath: "id"});
};
request.onsuccess = function() {
self.db = request.result;
};
request.onerror = function(e) {
console.log(e);
};
request.onblocked = function(e){
console.log('blocked')
}
},
addItem: function(store, loid, text){
var db = this.db;
var trans = db.transaction(store, "readwrite");
var store = trans.objectStore(store);
var request = store.put({
"id": loid,
"text" : text
});
request.onsuccess = function(e) {
// Re-render all the todo's
};
request.onerror = function(e) {
console.log(e.value);
};
},
getItem: function(store, loid){
var db = this.db;
var trans = db.transaction(store);
var store = trans.objectStore(store);
var request = store.get(loid);
request.onsuccess = function(e) {
console.log(request.result) // Refresh the screen
};
request.onerror = function(e) {
console.log(e);
};
},
removeItem: function(store, loid){
var db = this.db;
var trans = db.transaction(store, "readwrite");
var store = trans.objectStore(store);
var request = store.delete(loid);
request.onsuccess = function(e) {
console.log('el deleted'); // Refresh the screen
};
request.onerror = function(e) {
console.log(e);
};
},
testSize: function(){
var i = 0,
t;
while(i<100000){
t = new Date().getTime();
this.addItem('stories', i, t)
i++;
}
console.log('items added')
}
};
testIndexedDB.openDB();
simply run testIndexedDB.testSize() to notice the issue.
How can I properly test continuos insertion and why is this happening?
thanks
The main problem that you have in the batch insert is the fact that you are opening 100000 transaction and that blocks the database. I've made some optimizations to your code for batch inserting and now the insert time is less than 5s.
Instead of opening separate transaction for every item, I group the items in arrays of 1000 items and then open a transaction for that batch. Now the number of transaction is reduced to 100. Here are all the changes:
First I created a batch insert Function:
addItems: function(store, items){
var db = this.db;
var trans = db.transaction(store, "readwrite"); //uses only one transaction per batch of 1000
trans.oncomplete = function(e){
console.log('batch inserted');
};
var store = trans.objectStore(store);
for(var i=0; i<items.length; i++){
var request = store.put(items[i]);
request.onerror = function(e) {
console.log(e.value);
};
}
},
And then I've changed the inserting function to send the data in batches:
testSize: function(){
var i = 0,
t;
tempList = [];
while(i<100000){
t = new Date().getTime();
tempList.push({
"id": i,
"text" : t
})
if(i> 0 && i% 1000 == 0){ //items are grouped into a array of 1000 items
this.addItems('stories', tempList.slice());
tempList = [];
}
i++;
}
console.log('items added');
}

db value becomes null or even on indexdb open it does not go into onsuccess or upgradeneeded functions

(function () {
var db;
function openDB(dbname, dbtable, keypath) {
var version = 1;
// open the database
var indexeddb_request = indexedDB.open( dbname, version ); // connect + open database
// if error
indexeddb_request.onerror = function ( event ) {
console.log( event.target );
console.trace();
}
// if success
indexeddb_request.onsuccess = function ( event ) {
console.log( event.target );
console.trace();
db = event.target.result;
}
// if onupgradeneeded
indexeddb_request.onupgradeneeded = function( event ) {
console.log( event.target );
console.trace();
db = event.target.result;
var objectStore = db.createObjectStore( dbtable, { keyPath: 'url' } );
}
}
function getObjectStore(store_name, mode) {
var tx = db.transaction(store_name, mode);
return tx.objectStore(store_name);
}
function addPublication(dbtable, data) {
console.log("addPublication arguments:", arguments);
var store = getObjectStore(dbtable, 'readwrite');
var req;
try {
for (var i in data) {
req = store.add(data[i]);
}
//req = store.add(data);
} catch (e) {
if (e.name == 'DataCloneError')
displayActionFailure("This engine doesn't know how to clone a Blob, " +
"use Firefox");
throw e;
}
req.onsuccess = function (evt) {
console.log("Insertion in DB successful");
};
req.onerror = function() {
console.error("addPublication error", this.error);
};
}
function displayPubList(store, store_name) {
console.log("displayPubList");
if (typeof store == 'undefined')
store = getObjectStore(store_name, 'readonly');
var req;
req = store.count();
// Requests are executed in the order in which they were made against the
// transaction, and their results are returned in the same order.
// Thus the count text below will be displayed before the actual pub list
// (not that it is algorithmically important in this case).
req.onsuccess = function(evt) {
console.log("Count of records " + evt.target.result +
' record(s) in the object store.');
};
req.onerror = function(evt) {
console.error("add error", this.error);
};
var i = 0;
req = store.openCursor();
req.onsuccess = function(evt) {
var cursor = evt.target.result;
// If the cursor is pointing at something, ask for the data
if (cursor) {
console.log("displayPubList cursor:", cursor);
req = store.get(cursor.key);
req.onsuccess = function (evt) {
var value = evt.target.result;
console.log("Key :" + cursor.key + 'value :' + value.ssn );
};
// Move on to the next object in store
cursor.continue();
// This counter serves only to create distinct ids
i++;
} else {
console.log("No more entries");
}
};
}
openDB("abc","names","ssn");
const customerData = [
{ ssn: "444-44-4444", name: "Bill", age: 35, email: "bill#company.com" },
{ ssn: "555-55-5555", name: "Donna", age: 32, email: "donna#home.org" }
];
addPublication("names",customerData);
displayPubList("abc","names");
})();
I'm trying to test the above code from chrome developer tools. But looks like some cases the call goes into sucess or upgradeneeded. And many times its always null. Please let me know if i'm doing something wrong here.

Getting a "setVersion" is not a function error when using indexedDB

I tried to use following code to test the performance of IndexedDB.
The code is modified from http://www.html5rocks.com/en/tutorials/indexeddb/todo/ ,
It works well in chrome, but fails in Firefox 10, saying "db.setVersion is not a function".
I want to know how can I modify the code to make it work in firefox?
var count=0;
var MAX=10;
var times=3;
var allTime;
var stime;
var etime;
var html5rocks = {};
var indexedDB = window.indexedDB || window.webkitIndexedDB ||
window.mozIndexedDB;
if ('webkitIndexedDB' in window) {
window.IDBTransaction = window.webkitIDBTransaction;
window.IDBKeyRange = window.webkitIDBKeyRange;
}
html5rocks.indexedDB = {};
html5rocks.indexedDB.db = null;
html5rocks.indexedDB.onerror = function(e) {
//console.log(e);
alert("Why didn't you allow my web app to use IndexedDB?!");
};
html5rocks.indexedDB.open = function(type) {
var request = indexedDB.open("todos");
request.onsuccess = function(e) {
var v = "1.20";
html5rocks.indexedDB.db = e.target.result;
var db = html5rocks.indexedDB.db;
// We can only create Object stores in a setVersion transaction;
if (v!= db.version) {
var setVrequest = db.setVersion(v);
// onsuccess is the only place we can create Object Stores
setVrequest.onerror = html5rocks.indexedDB.onerror;
setVrequest.onsuccess = function(e) {
if(db.objectStoreNames.contains("todo")) {
db.deleteObjectStore("todo");
}
var store = db.createObjectStore("todo",
{keyPath: "number"});
addTest();
};
}
else addTest();
};
request.onerror = html5rocks.indexedDB.onerror;
}
html5rocks.indexedDB.addTodo = function(todoText,num) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var data = {
"text": todoText,
"number": num
};
var request = store.put(data);
request.onsuccess = function(e) {
count++;
if(count>=times*MAX)
{
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf())/times)+"<br/>";
allTime=0;stime=new Date;count=0;
getTest();
}
};
request.onerror = function(e) {
console.log("Error Adding: ", e);
};
};
html5rocks.indexedDB.getTodo = function(id) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var request = store.get(id);
request.onsuccess = function(e) {
count++;
if(count>=times*MAX)
{
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf())/times)+"<br/>";
allTime=0;stime=new Date;count=0;
delTest();
}
};
request.onerror = function(e) {
console.log("Error getting: ", e);
};
};
html5rocks.indexedDB.deleteTodo = function(id) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var request = store.delete(id);
request.onsuccess = function(e) {
count++;
if(count>=times*MAX)
{
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf())/times)+"<br/>";
allTime=0;stime=new Date;count=0;
dataTest();
}
};
request.onerror = function(e) {
console.log("Error Adding: ", e);
};
};
html5rocks.indexedDB.addData = function(d) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var data={
"text":d,
"number":1
};
var request = store.put(data);
request.onsuccess = function(e) {
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf()))+"<br/>";
};
request.onerror = function(e) {
console.log("Error Adding: ", e);
};
};
function addTest() {
for(i=1;i<=times*MAX;i++)
html5rocks.indexedDB.addTodo(' ',i);
}
function getTest() {
for(i=1;i<=times*MAX;i++)
html5rocks.indexedDB.getTodo(Math.round(Math.random()*(MAX*times-1)+1));
}
function delTest() {
for(i=1;i<=times*MAX;i++)
html5rocks.indexedDB.deleteTodo(i);
}
function dataTest() {
data=' ';
for(i=1;i<=21;i++)
data=data+data;
stime=new Date
html5rocks.indexedDB.addData(data);
}
function init() {
stime=new Date;
allTime=0;
html5rocks.indexedDB.open();
}
The spec is not finalized. This is currently shipped as the property mozIndexedDB in Gecko and webkitIndexedDB in Chrome until the standard is finalized. So you have to write for moz also. Now this code is only for webkit.
https://developer.mozilla.org/en/IndexedDB
setVersion() is Deprecated
The new way is to define the version in the IDBDatabase.open() method
firefox from version 10.0 implements open() with the new specification in which an indexeddb database IDBDatabase version is set as the second parameter of the open() method
example
var v = "1.20";
var request = indexedDB.open("todos", v);
html5 indexeddb javascript
The problem here is not the one chosen as the correct answer.
The problem is that the IndexedDB examples on HTML5Rocks were written to the pre-January IndexeDB spec. The working group has since published a breaking change going from the setVersion API to the new onupgradedneeded style.
Here, Firefox is technically correct to fail. Star this issue if you want to see Chrome do the same.

Categories

Resources