How do I check if an indexedDB instance is open? - javascript

Suppose I have an instance of an indexedDB object. Is there a simple way of detecting if the object is currently in the 'open' state?
I've tried database.closePending and looking at other properties but do not see a simple property that tells me the state of the database.
I am looking to do this synchronously.
Doing something like attempting open a transaction on a database and checking if an exception occurs is not a reasonable solution for me.
I don't want to maintain an extra variable associated with the database instance.
Perhaps I am missing some simple function in the api? Is there some observable feature of the instance variable that I can quickly and easily query to determine state?
Stated a different way, can you improve upon the following implementation?
function isOpen(db) {
if(db && Object.prototype.toString.call(db) === '[object IDBDatabase]') {
var names = db.objectStoreNames();
if(names && names.length) {
try {
var transaction = db.transaction(names[0]);
transaction.abort();
return true;
} catch(error) {
}
}
}
}
Or this method?
var opened = false;
var db;
var request = indexedDB.open(...);
request.onsuccess = function() {
db = request.result;
opened = true;
};
function isOpen(db) {
return opened;
}
db.close();
opened = false;
Or this method?
var db;
var request = indexedDB.open(...);
request.onsuccess = function() {
db = request.result;
db.onclose = function() {
db._secret_did_close = true;
};
};
function isOpen(db) {
return db instanceof IDBDatabase && !db.hasOwnProperty('_secret_did_close');
}

There's nothing else in the API that tells you if a connection is closed. Your enumeration of possibilities is what is available.
Also note that there is no closePending property in the API. The specification text uses a close pending flag to represent internal state, but this is not exposed to script.
Doing something like attempting open a transaction on a database and checking if an exception occurs is not a reasonable solution for me.
Why? This is the most reliable approach. Maintaining extra state would not account for unexpected closure (e.g. the user has deleted browsing data, forcing the connection to close) although that's what the onclose handler would account for - you'd need to combine your 2nd and 3rd approaches. (close is not fired if close() is called by script)

You should create a request by using indexedDB.open and if the connection is open you will jump onsuccess method.
request = indexedDB.open('html5',1);
request.onsuccess = function() {
console.log('Database connected');
};
Example :
https://codepen.io/headmax/pen/BmaOMR?editors=1111
About how to close or how to known if the indexedDB is still open : I guess you need to implement all events on every transaction : for example to take the control you can take the events : transaction.onerror, transaction.onabort ... If you need some example explanation i guess you have to create a new post ;).
https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction

Related

Avoid XMLHttpRequest chain calling leak

In my code (a monitoring application) I need to periodically call the server with an XMLHttpRequest object in the form of chained calls. Each call takes exactly 15 seconds, which is timed by the server as it delivers several partial results within that period (HTTP 100 Continue). Immediately after finishing the current call, the onreadystatechange event handler of the current XMLHttpRequest object needs to create and launch the next request (with a new instance), so the communication with the server remains almost seamless.
The way it works, each call retains the object context of the caller in the stack, so as this is a page that must remain open for days, the stack keeps growing with no chance for the garbage collector to claim the data. See the following stack trace:
I cannot use timers (setInterval or such) to launch the next request. It should be launched from inside the ending of the previous one. The data from server must arrive as quickly as possible, and unfortunately browsers nowadays throtle timers when a page is not in focus. As I said, this is a monitoring application meant to be always on in the users' secondary monitors (rarely in focus). I also need to deal with HTTP timeouts and other kinds of errors that derail from the 15 second sequence. There should always be one and only one channel open with the server.
My question is whether is any way to avoid keeping the whole context in the stack when creating an XMLHttpRequest object. Even calling the click() method on a DOM object will keep the stack/context alive. Even promises seem to keep the context.
I'm also unable to use websockets, as the server does not support them.
UPDATE:
It's more complex, buy in essence it's like:
var xhttpObjUrl;
var xhttpObj;
onLoad() {
loadXMLDoc(pollURL + "first=1", true);
}
function loadXMLDoc(url, longtout) {
xhttpObjUrl = url;
xhttpObj = new XMLHttpRequest();
xhttpObj.open(method, url, true);
xhttpObj.onprogress = progress;
xhttpObj.onloadend = progress;
xhttpObj.ontimeout = progress;
if (commlog) consolelog("loadXMLDoc(): url == " + dname);
xhttpObj.send("");
}
function progress() {
if (!xhttpObj) return;
var state = xhttpObj.readyState;
var status;
var statusText;
if (state == 4 /* complete */ || state == 3 /* partial content */) {
try {
status = xhttpObj.status;
statusText = xhttpObj.statusText;
if (status == 200) parseServerData();
} catch (err) {
status = 500;
statusText = err;
}
if (state == 4 || status != 200) {
/* SERVER TERMINATES THE CONNECTION AFTER 15 SECONDS */
/* ERROR HANDLING REMOVED */
var obj = xhttpObj;
xhttpObj = undefined;
abortRequest(obj);
obj = false;
RequestEnd();
}
}
}
function RequestEnd(error) {
var now = (new Date).getTime();
var msdiff = now - lastreqstart;
var code = function () { loadXMLDoc(pollURL + 'lastpoint=' + evtprev.toString() + '&lastevent=' + evtcurrent.toString()); return false; };
if (msdiff < 1000) addTimedCheck(1, code); /** IGNORE THIS **/
else code();
}
I've solved my problem using a web worker. The worker would end the XMLHttpRequest each time and send the page a message with the collected data. Then, when the page finishes processing the data, it would send the worker a message to start a new request. Thus my page wouldn't have any unwanted delays between requests, and there's no stack constantly building up. On error I'd terminate the worker and create a new one, just in case.

PhoneGap Windows Phone 8 IndexedDB AbortError when opening db

Using essentially the example from the MDN IndexedDb tutorial I can see that my test IndexedDb code is working on Chrome. When I load the app onto my Windows Phone 8 device inside of the deviceready handler, I get an AbortError in the error handler for the database open request.
The only other related SO question was solved by fixing errors in onupgradeneeded but this handler is never even called in my code.
In this simple example, you have to run the fiddle twice because apparently onsuccess is called (where I read a test value) before onupgradeneeded (where I write the value when the db is initialized). I was going to deal with this once I got this first test to work.
http://jsfiddle.net/WDUVx/2/
// In the following line, you should include the prefixes of
// implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
}
// open the database
var db;
var request = window.indexedDB.open("MyTestDatabase", 1);
request.onerror = function(e) {
alert("Couldn't open database: " + kc.util.getObjectString(e.target));
};
request.onsuccess = function(e) {
db = e.target.result;
var getRequest =
db.transaction("data")
.objectStore("data")
.get("firstObject")
.onsuccess = function(event) {
alert("Got: " + event.target.result.test);
};
};
request.onupgradeneeded = function(e) {
var db = event.target.result;
var objectStore = db.createObjectStore("data", {
autoincrement : false
});
objectStore.transaction.oncomplete = function(event) {
var myObjectStore = db.transaction("data", "readwrite").objectStore("data");
var addRequest = myObjectStore.add({
test : true
},
"firstObject");
addRequest.onerror = function(e) {
console.log("Error adding");
};
addRequest.onsuccess = function(e) {
console.log("Added!");
};
};
};
Questions:
What is my stupid mistake?
Are there any Windows Phone 8 examples of IndexedDb and PhoneGap? I could not find any after some searching. There were a few for an IndexedDb API Android and IOS polyfill, but none for wp8.
Is there something special that I have to do because I'm on a phone? Again, the code works in chrome.
Are there any other plugins that support wp8 storage > 5mb?
LocalStorage has a 5mb size limit
WebSQL is not supported
FileSystem plugin does not support filewriter.write(blob). This is what I am using for Android/iOS. It's strange that they say wp8 is supported by this plugin when this is the only way to actually write data, and you can't read the nothing you can write. I found that although the web api does not support it, the devices support filewriter.write(string). Windows Phone 8 is still not writing/reading things entirely correctly, but that is a separate question.
Recently, I faced a similar issue with indexed DB. My IndexedDB.open request was throwing an abort error.
After doing some search, I found suggestions to separate the requests for database creation and store creation.
Separating the code prevented the abort error. However, I noticed that the transaction to create the stores was sometimes run even before the completion of the database creation request.
This meant that my database connection was not closed from the first request, when the second request was run.
A minor fix was required to overcome this error. I moved the code to create stores to the success event for the first call.
Here is the code for reference.
function create_db(db_name)
{
var request = indexedDB.open(db_name);
request.onupgradeneeded=function(e)
{
console.log("1. creating database");
db=e.target.result;
};
request.onsuccess = function(e)
{
db = e.target.result;
console.log("1.1 database created successfully");
db.close();
add_tables(db_name);
};
request.onerror=function(e)
{
alert("error: "+ e.target.error.name + "failed creating db");
console.log("1.2 error creating db");
};
}
function add_tables(db_name)
{
var request = indexedDB.open(db_name,2);
request.onsuccess=function(e)
{
db=e.target.result;
console.log("2.2 table creation request successful");
};
request.onupgradeneeded=function(e)
{
db=e.target.result;
table = db.createObjectStore("table_name");
table.createIndex("id","id");
console.log("2.2 creating a single object store");
};
request.onerror=function(e)
{
console.log("2.3 error occured when creating tables");
};
};
Just some ideas, hope they help:
Don't use a global db variable. Do all of your work in callbacks. Using a global db variable can lead to numerous in-explainable situations, some of which include getting abort errors. Looking at your code, it actually looks like you are properly just using e.target, so I am not sure why you have a global var db.
Don't perform read/write requests on the version change transaction that occurs in the onupgradeneeded callback. Instead, perform requests when they are appropriate and let indexeddb worry about calling onupgradeneeded. In other words, don't retrieve the transaction in onupgradeneeded. Instead, just initiate some later transaction in a new connection as if the onupgradeneeded callback already completed.
openDBRequest having an abort event precedes onupgradeneeded callback
onupgradeneeded won't be called unless you make an attempt to connect to a database using a higher version
Listen for abort events. Add a callback to the open database request for onabort. Abort events sometimes occur because of things like opening two pages in the same context that try to access the same database. There could be something funky going on there.

Windows 8 App Development using Dhtmlxscheduler

I am writing a notification application which based on DHTMLxScheduler.
I would like to know more abut the idea of CRUD by IndexedDB for DHTMLxscheduler
As far I know, the following website shows an excellent example
http://www.codeproject.com/Articles/594924/Build-Calendar-App-for-Windows-8-with-dhtmlxSchedu
However, the data store is not persistent and the application would freeze during multi-touch event.
Does anyone can help to direct the coding needed for the CRUD by its default IndexedDB using the following?
scheduler.attachEvent("onEventDeleted",
function(event_id,event_object){
//add event to the data store
});
scheduler.attachEvent("onEventChanged", function(event_id, event_object){
//update event in the data store
});
scheduler.attachEvent("onEventAdded", function(event_id, event_object){
//delete event from the data store
});
The following example shows how to integrate by IndexedDB
http://www.dotnetcurry.com/ShowArticle.aspx?ID=868
However, they shares different framework, while the original scheduler sample used callback always to detect changes.
Thanks a lot for your help!
IndexedDB takes relatively lot of code for CRUD operation, so tutorial has been seriously simplified in order not to overload it with implementation details.
There is also complete example with working CRUD, check the '/samples/CalendarApp/' folder of this package:
http://dhtmlx.com/x/download/regular/dhtmlxScheduler_windows.zip
As for multi-touch issue, most probably it will be fixed in the nearest time. Current version of the package is based on dhtmlxScheduler3.7, we are going to update it to 4.0 which has an improvements for windows based touch devices.
And here is a example of database handling, similary to how it's done in the app from dhtmlx site.
//connect to indexedDb and fire the callback on success
function connect(callback){
try{
var db = null;
var req = window.indexedDB.open("SchedulerApp", 1);
req.onsuccess = function (ev) {
db = ev.target.result;
if(callback)//fire a callback on connect
callback(db);
}
req.onupgradeneeded = function(e){
//The event is fired when connecting to the new database, or on version change.
//This is the only place for defining database structure(object stores)
var db = ev.target.result;
if (!db.objectStoreNames.contains("events")) {
//create datastore, set 'id' as autoincremental key
var events = db.createObjectStore("events", { keyPath: "id", autoIncrement: true });
}
}
}catch(e){
}
}
//add js object to the database and fire callback on success
function insertEvent(data, callback) {
connect(function (db) {
var store = db.transaction("events", "readwrite").objectStore("events");
var updated = store.add(data);
updated.onsuccess = function (res) {
callback(res.target.result);
}
});
}
// use all defined above with the dhtmlxScheduler
// when user adds an event into the scheduler - it will be saved to the database
scheduler.attachEvent("onEventAdded", function (id) {
var ev = copyEvent(scheduler.getEvent(id));//where copyEvent is a helper function for deep copying
delete ev.id;//real id will be assigned by the database
insertEvent(ev, function (newId) {
scheduler.changeEventId(id, newId);//update event id in the app
});
return true;
});
However, I can't guarantee it will work right away, I can't test the code at the moment.
I'd also suggest you to check these articles on MSDN
FYI, I work for DHTMLX

Error: The page has been destroyed and can no longer be used

I'm developing an add-on for the first time. It puts a little widget in the status bar that displays the number of unread Google Reader items. To accommodate this, the add-on process queries the Google Reader API every minute and passes the response to the widget. When I run cfx test I get this error:
Error: The page has been destroyed and can no longer be used.
I made sure to catch the widget's detach event and stop the refresh timer in response, but I'm still seeing the error. What am I doing wrong? Here's the relevant code:
// main.js - Main entry point
const tabs = require('tabs');
const widgets = require('widget');
const data = require('self').data;
const timers = require("timers");
const Request = require("request").Request;
function refreshUnreadCount() {
// Put in Google Reader API request
Request({
url: "https://www.google.com/reader/api/0/unread-count?output=json",
onComplete: function(response) {
// Ignore response if we encountered a 404 (e.g. user isn't logged in)
// or a different HTTP error.
// TODO: Can I make this work when third-party cookies are disabled?
if (response.status == 200) {
monitorWidget.postMessage(response.json);
} else {
monitorWidget.postMessage(null);
}
}
}).get();
}
var monitorWidget = widgets.Widget({
// Mandatory widget ID string
id: "greader-monitor",
// A required string description of the widget used for
// accessibility, title bars, and error reporting.
label: "GReader Monitor",
contentURL: data.url("widget.html"),
contentScriptFile: [data.url("jquery-1.7.2.min.js"), data.url("widget.js")],
onClick: function() {
// Open Google Reader when the widget is clicked.
tabs.open("https://www.google.com/reader/view/");
},
onAttach: function(worker) {
// If the widget's inner width changes, reflect that in the GUI
worker.port.on("widthReported", function(newWidth) {
worker.width = newWidth;
});
var refreshTimer = timers.setInterval(refreshUnreadCount, 60000);
// If the monitor widget is destroyed, make sure the timer gets cancelled.
worker.on("detach", function() {
timers.clearInterval(refreshTimer);
});
refreshUnreadCount();
}
});
// widget.js - Status bar widget script
// Every so often, we'll receive the updated item feed. It's our job
// to parse it.
self.on("message", function(json) {
if (json == null) {
$("span#counter").attr("class", "");
$("span#counter").text("N/A");
} else {
var newTotal = 0;
for (var item in json.unreadcounts) {
newTotal += json.unreadcounts[item].count;
}
// Since the cumulative reading list count is a separate part of the
// unread count info, we have to divide the total by 2.
newTotal /= 2;
$("span#counter").text(newTotal);
// Update style
if (newTotal > 0)
$("span#counter").attr("class", "newitems");
else
$("span#counter").attr("class", "");
}
// Reports the current width of the widget
self.port.emit("widthReported", $("div#widget").width());
});
Edit: I've uploaded the project in its entirety to this GitHub repository.
I think if you use the method monitorWidget.port.emit("widthReported", response.json); you can fire the event. It the second way to communicate with the content script and the add-on script.
Reference for the port communication
Reference for the communication with postMessage
I guess that this message comes up when you call monitorWidget.postMessage() in refreshUnreadCount(). The obvious cause for it would be: while you make sure to call refreshUnreadCount() only when the worker is still active, this function will do an asynchronous request which might take a while. So by the time this request completes the worker might be destroyed already.
One solution would be to pass the worker as a parameter to refreshUnreadCount(). It could then add its own detach listener (remove it when the request is done) and ignore the response if the worker was detached while the request was performed.
function refreshUnreadCount(worker) {
var detached = false;
function onDetach()
{
detached = true;
}
worker.on("detach", onDetach);
Request({
...
onComplete: function(response) {
worker.removeListener("detach", onDetach);
if (detached)
return; // Nothing to update with out data
...
}
}).get();
}
Then again, using try..catch to detect this situation and suppress the error would probably be simpler - but not exactly a clean solution.
I've just seen your message on irc, thanks for reporting your issues.
You are facing some internal bug in the SDK. I've opened a bug about that here.
You should definitely keep the first version of your code, where you send messages to the widget, i.e. widget.postMessage (instead of worker.postMessage). Then we will have to fix the bug I linked to in order to just make your code work!!
Then I suggest you to move the setInterval to the toplevel, otherwise you will fire multiple interval and request, one per window. This attach event is fired for each new firefox window.

Gdata JavaScript Authsub continues redirect

I am using the JavaScript Google Data API and having issues getting the AuthSub script to work correctly. This is my script currently:
google.load('gdata', '1');
function getCookie(c_name){
if(document.cookie.length>0){
c_start=document.cookie.indexOf(c_name + "=");
if(c_start!=-1){
c_start=c_start + c_name.length+1;
c_end=document.cookie.indexOf(";",c_start);
if(c_end==-1) c_end=document.cookie.length;
return unescape(document.cookie.substring(c_start, c_end));
}
}
return "";
}
function main(){
var scope = 'http://www.google.com/calendar/feeds/';
if(!google.accounts.user.checkLogin(scope)){
google.accounts.user.login();
} else {
/*
* Retrieve all calendars
*/
// Create the calendar service object
var calendarService = new google.gdata.calendar.CalendarService('GoogleInc-jsguide-1.0');
// The default "allcalendars" feed is used to retrieve a list of all
// calendars (primary, secondary and subscribed) of the logged-in user
var feedUri = 'http://www.google.com/calendar/feeds/default/allcalendars/full';
// The callback method that will be called when getAllCalendarsFeed() returns feed data
var callback = function(result) {
// Obtain the array of CalendarEntry
var entries = result.feed.entry;
//for (var i = 0; i < entries.length; i++) {
var calendarEntry = entries[0];
var calendarTitle = calendarEntry.getTitle().getText();
alert('Calendar title = ' + calendarTitle);
//}
}
// Error handler to be invoked when getAllCalendarsFeed() produces an error
var handleError = function(error) {
alert(error);
}
// Submit the request using the calendar service object
calendarService.getAllCalendarsFeed(feedUri, callback, handleError);
}
}
google.setOnLoadCallback(main);
However when I run this the page redirects me to the authentication page. After I authenticate it send me back to my page and then quickly sends me back to the authenticate page again. I've included alerts to check if the token is being set and it doesn't seem to be working. Has anyone has this problem?
I was having the same problem so I built this function
function login() {
var scope = "http://www.google.com/calendar/feeds/";
if(!google.accounts.user.checkLogin(scope)) {
if(google.accounts.user.getStatus() == 0) {
var token = google.accounts.user.login();
}
}
}
I added the check to google.accounts.user.getStatus() if it's 1 that means the application is in the process of logging in and if it is 2 that means the applications is logged in. You can also pass a scope to the getStatus method.
the problem is that setting the cookie takes a little while when google redirects back to your site. However, the callback runs immediately, and there is no cookie by that time to verify authentication, so it again redirects back to google. Try using setTimeout or something to run the authentication check after a second or so to be sure.
You should pass the scope to the login method too.
Sometimes you can end up with an orphaned cookie in your browser - which will keep getting fed back to Google.
What I'm doing now, is doing a checkLogin before I perform my login call, and if it returns true I explicitly call logOut().
The logOut call will remove any cookies which Google had rejected but left in your browser. The reason it seems to keep going in a loop is because the cookie is there, but even on reauth, it doesn't produce a new one because you already have one. But unfortunately for our sake, the one that's there is invalid.

Categories

Resources