indexedDb objectStore.openCursor so slow - javascript

The first time using the index query takes too long.
usage scenarios: mobile use webview.
After the data is saved into indexedDb, the first time you open the page query is extremely slow.
query code:
var startTime = new Date().getTime();
var request = indexedDB.open("yfg");
request.onerror = function(event) {
alert("Why didn't you allow my web app to use IndexedDB?!");
};
request.onsuccess = function(event) {
var table = [];
var db = request.result;
var objectStore = db.transaction("table").objectStore("table");
//objectStore.openCursor().onsuccess = function(event) {
objectStore.openCursor(null,IDBCursor.NEXT).onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
table.push(cursor.value);
cursor.continue();
}else {
//alert("No more entries!");
console.log(table);
var endTime = new Date().getTime();
console.log("总耗时:",(endTime-startTime)/1000);
}
};
};
Execute on console:
The first result:
The second result:
Browser version:
The specific content of each piece of data:
This table sheet has these data:

Try using IDBObjectStore.prototype.getAll. getAll generates an array, similar to what you are doing, but involves fewer function calls than openCursor and is therefore more performant.
However, unlike openCursor, getAll may not be supported because it is a newer feature of indexedDB. The linked page shows a compatibility table but that may also be out of date.

Related

Using MP4box.js and onSegment callback is not called

Base problem: display a H264 live stream in a browser.
Solution: let's just convert it to fragmented mp4 and load chunk-by-chunk via websocket (or XHR) into MSE.
Sounds too easy. But I want to do the fragmentation on client side with pure JS.
So I'm trying to use MP4Box.js. On its readme page it states: it has a demo: "A player that performs on-the-fly fragmentation".
That's the thing I need!
However the onSegment callbacks which should feed MSE are not called at all:
var ws; //for websocket
var mp4box; //for fragmentation
function startVideo() {
mp4box = MP4Box.createFile();
mp4box.onError = function(e) {
console.log("mp4box failed to parse data.");
};
mp4box.onMoovStart = function () {
console.log("Starting to receive File Information");
};
mp4box.onReady = function(info) {
console.log(info.mime);
mp4box.onSegment = function (id, user, buffer, sampleNum) {
console.log("Received segment on track "+id+" for object "+user+" with a length of "+buffer.byteLength+",sampleNum="+sampleNum);
}
var options = { nbSamples: 1000 };
mp4box.setSegmentOptions(info.tracks[0].id, null, options); // I don't need user object this time
var initSegs = mp4box.initializeSegmentation();
mp4box.start();
};
ws = new WebSocket("ws://a_websocket_server_which_serves_h264_file");
ws.binaryType = "arraybuffer";
ws.onmessage = function (event) {
event.data.fileStart = 0; //tried also with event.data.byteOffset, but resulted error.
var nextBufferStart = mp4box.appendBuffer(event.data);
mp4box.flush(); //tried commenting out - unclear documentation!
};
}
window.onload = function() {
startVideo();
}
Now putting this into an HTML file would result this in the JavaScript console:
Starting to receive File Information
video/mp4; codecs="avc1.4d4028"; profiles="isom,iso2,avc1,iso6,mp41"
But nothing happens afterwards. Why is the onSegment not called here? (the h264 file which the websocket-server serves is playable in VLC - however it is not fragmented)
The problem was using the nextBufferStart in a wrong way.
This should be the correct one:
var nextBufferStart = 0;
...
ws.onmessage = function (event) {
event.data.fileStart = nextBufferStart;
nextBufferStart = mp4box.appendBuffer(event.data);
mp4box.flush();
};

How To Add Index to Pre-Existing ObjectStore In IndexedDB

I know this questions has been asked several times . But I have not been able to find out the solution after getting error multiple times . this is the code of my indexed db
request.onupgradeneeded = function(event) {
var db = event.target.result;
var upgradeTransaction = event.target.transaction;
var objectStore = db.createObjectStore("todostore", {keyPath: "timestamp"});
UserFunction();
};
function UserFunction(){
var ObjectStore = db.transaction("todostore").objectStore("todostore");
var index = ObjectStore.createIndex("ixName", "fieldName");
}
Failed to execute 'createIndex' on 'IDBObjectStore': The database is not running a version change transaction.
I am calling this function of button click I want to add index with value when a button is clicked
<button onclick="UserFunction()">createIndex</button>
You can only change the schema of the database during a version upgrade. Something like this is plausible:
function OnClick() {
// assumes db is a previously opened connection
var oldVersion = db.version;
db.close();
// force an upgrade to a higher version
var open = indexedDB.open(db.name, oldVersion + 1);
open.onupgradeneeded = function() {
var tx = open.transaction;
// grab a reference to the existing object store
var objectStore = tx.objectStore('todostore');
// create the index
var index = objectStore.createIndex('ixName', 'fieldName');
};
open.onsuccess = function() {
// store the new connection for future use
db = open.result;
};
}
Code in UserFunction() call, is starting a new transaction, while already a transaction is going on in "upgradeneeded" listener.
So new transaction should be started, after objectStore.transaction completes.
Here is the JSFiddle : Solution is here
function UserFunction(){
var request = window.indexedDB.open("MyTestDatabase", 3);
request.onupgradeneeded = function(event) {
var db = event.target.result;
var upgradeTransaction = event.target.transaction;
var objectStore = db.createObjectStore("todostore", {keyPath: "timestamp"});
objectStore.transaction.oncomplete = function(event) {
addIndex(db);
};
};
}
function addIndex(db){
var ObjectStore = db.transaction("todostore").objectStore("todostore");
var index = ObjectStore.createIndex("ixName", "fieldName");
}

How to change emscripten browser input method from window.prompt to something more sensible?

I have a C++ function which once called consumes input from stdin. Exporting this function to javascript using emscripten causes calls to window.prompt.
Interacting with browser prompt is really tedious task. First of all you can paste only one line at time. Secondly the only way to indicate EOF is by pressing 'cancel'. Last but not least the only way (in case of my function) to make it stop asking user for input by window.prompt is by checking the checkbox preventing more prompts to pop up.
For me the best input method would be reading some blob. I know I can hack library.js but I see some problems:
Reading blob is asynchronous.
To read a blob, first you have to open a file user has to select first.
I don't really know how to prevent my function from reading this blob forever - there is no checkbox like with window.prompt and I'm not sure if spotting EOF will stop it if it didn't in window.prompt case (only checking a checkbox helped).
The best solution would be some kind of callback but I would like to see sime hints from more experienced users.
A way would be to use the Emscripten Filesystem API, for example by calling FS.init in the Module preRun function, passing a custom function as the standard input.
var Module = {
preRun: function() {
function stdin() {
// Return ASCII code of character, or null if no input
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
The function is quite low-level: is must deal with one character at a time. To read some data from a blob, you could do something like:
var data = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
var result;
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
});
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (if < result.byteLength {
var code = result[i];
++i;
return code;
} else {
return null;
}
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
Note (as you have hinted), due to the asynchronous nature of the reader, there could be a race condition: the reader must have loaded before you can expect the data at the standard input. You might need to implement some mechanism to avoid this in a real case. Depending on your exact requirements, you could make it so the Emscripten program doesn't actually call main() until you have the data:
var fileRead = false;
var initialised = false;
var result;
var array = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
fileRead = true;
runIfCan();
});
reader.readAsArrayBuffer(blob);
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (i < result.byteLength)
{
var code = result[i];
++i;
return code;
} else{
return null;
}
}
var stdout = null;
var stderr = null;
FS.init(stdin, stdout, stderr);
initialised = true;
runIfCan();
},
noInitialRun: true
};
function runIfCan() {
if (fileRead && initialised) {
// Module.run() doesn't seem to work here
Module.callMain();
}
}
Note: this is a version of my answer at Providing stdin to an emscripten HTML program? , but with focus on the standard input, and adding parts about passing data from a Blob.
From what I understand you could try the following:
Implement selecting a file in Javascript and access it via Javascript Blob interface.
Allocate some memory in Emscripten
var buf = Module._malloc( blob.size );
Write the content of your Blob into the returned memory location from Javascript.
Module.HEAPU8.set( new Uint8Array(blob), buf );
Pass that memory location to a second Emscripten compiled function, which then processes the file content and
Deallocate allocated memory.
Module._free( buf );
Best to read the wiki first.

Store retrieved data from indexed DB to a variable

I am using the following code to read data from Indexed DB and save it in variable allDownloadContent
ereaderdownload.indexedDB.getAllTodoItems = function() {
/*var todos = document.getElementById("todoItems");
todos.innerHTML = "";
*/
var db = ereaderdownload.indexedDB.db;
var trans = db.transaction(["downloadcontent"], "readwrite");
var store = trans.objectStore("downloadcontent");
var request = store.get(0);
request.onsuccess = function(e) {
console.log(e.target.result);
};
// Get everything in the store;
var cursorRequest = store.openCursor();
cursorRequest.onsuccess = function(e) {
var result = e.target.result;
if(!!result == false)
return;
allDownloadContent.push(result);
result.continue();
};
alert("content "+allDownloadContent[0]);
cursorRequest.onerror = ereaderdownload.indexedDB.onerror;
};
When I call the getAllTodoItems method from another Javascript file I am getting a alert message content undefined
since the cursorRequest.onsuccess method executes async I am getting undefined.
I cannot make use of web workers since it is not supported in chrome.
I tried promise in Jquery. Still I am getting the same alert message.
Please help me in resolving the issue.
As for now all browsers only support the Indexed-db ASync API, and what you need to do is add an event listener to the transaction oncomplete event. This event will fire when cursor is closed. From there you can return to your code:
trans.oncomplete = function (event) {
console.log('transaction completed');
yourFunction();
};

Error "A mutation operation was attempted on a database that did not allow mutations." when retrieving data in indexedDB

I have this simple example code:
var request = mozIndexedDB.open('MyTestDatabase');
request.onsuccess = function(event){
var db = event.target.result;
var request = db.setVersion('1.0');
request.onsuccess = function(event){
console.log("Success version.");
if(!db.objectStoreNames.contains('customers')){
console.log("Creating objectStore");
db.createObjectStore('customers', {keyPath: 'ssn'});
}
var transaction = db.transaction([], IDBTransaction.READ_WRITE, 2000);
transaction.oncomplete = function(){
console.log("Success transaction");
var objectStore = transaction.objectStore('customers');
};
};
};
I am getting this:
A mutation operation was attempted on a database that did not allow mutations." code: "6
on line
var objectStore = transaction.objectStore('customers');
Can't figure out - what do I do wrong?
You can create or delete an object store only in a versionchange transaction
see: https://developer.mozilla.org/en-US/docs/IndexedDB/IDBDatabase
I think I found the answer. I shouldn't access objectStore inside oncomplete. I just need to do it after making new transaction. Right way is this:
var transaction = db.transaction([], IDBTransaction.READ_WRITE, 2000);
transaction.oncomplete = function(){
console.log("Success transaction");
};
var objectStore = transaction.objectStore('customers');
Btw, this is how exactly Mozilla's MDN shows. https://developer.mozilla.org/en/IndexedDB/Using_IndexedDB#section_10
I didn't try that code but judging by the documentation you shouldn't pass an empty list as first parameter to db.transaction() - it should rather be db.transaction(["customers"], ...) because you want to work with that object store.

Categories

Resources