I was messing around with IndexedDB and I realised that I don't really get event handling in JavaScript.
So here's the code:
var request = indexeddb.open(bla, version);
request.onsuccess = function (event) { };
So the open-method returns a IDBOpenDBRequest object, which, according to Mozillas site, inherits from IDBRequest, which apart from properties and methods also has event handlers, one of them being onsuccess:
https://developer.mozilla.org/en-US/docs/Web/API/IDBRequest.onsuccess
So on the mozilla site, onsuccess is just function () { }
Now, when the database was opened sucessfully, the "onsuccess" event fires and the appropiate event handler is called, in this case the function that I defined. But how exactly does that happen?
The request variable contains an instance of the IDBOpenDBRequest. So when I write request.onsuccess = somefunction(), am I overwriting the default function of the IDBRequest-class?
I dont get why I can write request.onsuccess = somefunction(event) { } and how the event is passed to that function.
EDIT:
function myObect() {
this.open = function(a,b,c) {
if (c > 20) {
this.success("String");
}
};
};
var myrequest = new myObect();
myrequest.open(4,2,21);
myrequest.success = function (ev) {
console.log(ev);
};
To create a similar api, you can do something like:
function open(a, b, c) {
var request = {};
if(c > 20) {
setTimeout(function() {
if(typeof request.success === "function") {
request.success("String");
}
}, 1);
}
return request;
}
var myrequest = open(4, 2, 21);
myrequest.success = function(ev) {
console.log(ev);
};
Here, setTimeout is asynchronous so the callback function is not executed immediately. When any asynchronous task is run in JavaScript, the currently executing code will run to completion before any callback is called. So success is guaranteed to be set before request.success called.
The Indexed DB open call similarly runs an asynchronous task, and then dispatches events when it is finished which will eventually call your callback function.
I overwriting the default function of the IDBRequest-class
Looks like there is no default behavior, so you just set up your own func.
Related
I have a problem with a singleton pattern that I have implemented in 'program.js':
var Program = (function() {
var _program; // Instance of program
// Constructor
function Program() {
if (typeof _program != "undefined") {
throw new Error("Program can only be instantiated once.");
}
this.run = false; // flag to allow for program exec
_program = this;
};
// Public methods
Program.prototype.init = function() {
// the run call is nested in 4 callbacks
callbackHell (
this.run = true;
);
};
Program.prototype.execute = function() {
if(this.run == true) {
// do stuff
}
};
Program.getProgram = function () {
if(typeof _program == "undefined")
{
return new this();
}
return _program;
};
// Return the constructor
return Program;
})();
In my 'main.js' I keep a reference to the loaded program and check for the run flag to allow execution. Looks like this:
var program = null;
function initDemo() {
program = Program.getProgram();
program.init();
runDemo();
};
function runDemo() {
if(program != null && program.run) {
program.execute();
}
requestAnimationFrame(runDemo);
};
If I execute this code on a Chrome browser, it will never reach the program.execute() call in main.js. The reference of program will keep the run flag to false, even it is changed in the init() function. I checked for all of this in the debugger. I should point out that the 'this.run = true' call is nested in 4 callbacks. After a while I figured I could just change the run flag of the global reference of program in main.js with the init() function. So instead of 'this.run = true', 'program.run = true'. This works and the loop will run execute(). However this is not the style I am used from OOP. What is actually happening here? It definitely has to do with the callbacks: when I put 'this.run = true' out of the callbacks at the end of init() the flag is changed correctly, however at the wrong time of the program execution.
Probably your callbacks in the callbackHell are doing something asynchronously and there is a delay before program.run will actually be set to true, the sequence is approximately this:
You call program.init()
Your callbacks start working
You call runDemo(), here program.run is false and it exists
Callbacks finish their work and program.run becomes true
The solution to that is to make your runDemo to be another callback, so your main code will look like this:
var program = null;
function initDemo() {
program = Program.getProgram();
program.init(runDemo);
};
function runDemo() {
if(program != null) {
program.execute();
}
requestAnimationFrame(runDemo);
};
Here you don't need the program.run flag at all, instead you just start your demo from inside the "callbackHell":
Program.prototype.init = function(functionToRun) {
// the run call is nested in 4 callbacks
callbackHell (
functionToRun(); // instead of setting program.run
// we call the specified function
);
};
I have share variable between javascript function which is asynchronous. One of them is main thread and another is event based. I want to return value when event is completed.
This is the code:
completeExecution = false; // Shared Variable (Global Variable)
indexDBdata = {}; // Shared Variable (Global Variable)
function getPermission(key) {
var permission_data={};
if(exist_in_local) {
indexdbConnection.getRecordByKey('userPermission',permitKey,function(data){
indexDBdata=data; // Before its complete function return value
});
} else {
// make ajax call & its working fine
}
return permission_data;
}
//get Data from IndexedDB
getRecordByKey:function(tableName,key,readRecords){
if(isEmptyOrNull(readRecords)){
console.log("callback function should not be empty");
return;
}
if(isEmptyOrNull(tableName)){
console.log("table name should not be empty");
return;
}
var returnObj={};
var isSuccessfull=false;
if(this.dbObject.objectStoreNames.contains(tableName)){
var transaction=this.dbObject.transaction(tableName);
var objectStore = transaction.objectStore(tableName);
objectStore.get(key).onsuccess = function(event) {
returnObj=event.target.result;
};
**//Return object after this events compelte**
transaction.oncomplete = function(evt) {
completeExecution=true;
indexDBdata=returnObj;
readRecords(returnObj);
};
transaction.onerror = function(evt) {
completeExecution=true;
indexDBdata={status:'404'};
readRecords("Table Not found");
};
} else {
completeExecution=true;
indexDBdata={status:'404'};
readRecords("Table Not found");
}
}
Problem is while retrieving data from indexedDB it always returns {} (empty object). I want to synchronised event thread and main thread or wait for event to be completed. I don't want to directly manipulate DOM on callbacks I have to return value.
If you have solution to above problem or any other trick then please help me.
Thanks in advance.
I don't find the question very clear, but if I understand it, then you need to learn more about writing asynchronous javascript. In general, functions that call callback functions are void (they return an undefined value). If you want to use the results of two callback functions together, then you will want to chain them so that upon the completion of the first function, which calls its callback function, the callback function then calls the second function which then calls the second callback. So there are four function calls involved. You will want to place the processing logic within the context of the successive callback function, instead of continuing the logic outside of the function and trying to use its return value.
In other words, instead of trying to do this:
function a() {}
function b() {}
var aresult = a();
var bresult = b(aresult);
// processing of both a and b
You would want to try and do something like following:
function a(acallback) {
acallback(...);
}
function b(bcallback) {
bcallback(...);
}
a(function(...) {
b(function(...) {
// all processing of both a and b
});
});
I set up a callback function inside my Meteor async method to be called on "readable" event. But the callback is not being called when the on."readable" is being fired (I know it's being fired from the console.log I set up).
Am I missing something here? I've been at it for a few hours now trying a few different things!
Meteor.startup(() => {
Meteor.call("getfeed", function(feedloader) {
//I get: TypeError: undefined is not a function]
console.log(feedloader);
});
});
Meteor.methods({
getfeed: function(callb) {
var req = request('http://feeds.feedburner.com/Techcrunch');
var feedparser = new FeedParser();
testing = [];
//........a bunch of functions........
feedparser.on('readable', function() {
var stream = this
, meta = this.meta
, item;
while (item = stream.read())
{
//I'm pushing the results into testing var
testing.push(item);
}
//From the logs I can see that this is called 12 times
//but the callback's not firing!!!
console.log(testing.length);
callb(testing);
});
}
});
Meteor methods are not asynchronous functions in the sense that they do not get the callback argument even though you pass it when you "call" a method. Instead each method is executed within a Fiber which is another flavor of dealing with asynchronous code.
Fortunately, Meteor has a nice helper that allows you to mix both styles. What you need to do is wrap the "pure" asynchronous part of your method code with Meteor.wrapAsync. This structure should look more or less like this:
Meteor.methods({
getfeed: function() {
var wrapped = Meteor.wrapAsync(function (callb) {
var feedparser = new FeedParser();
testing = [];
// ...
feedparser.on('readable', function() {
// probably the same code you have, but without "callb()"
});
feedparser.on('end', function () {
// NOTE: No error here, so the first argument must be null.
callb(null, testing);
})
});
// NOTE: Finally, call the wrapped function
return wrapped();
}
});
I have a close function that will close some instance. The class that includes the function allows derived classes to override close. Here, I want to make sure that close always calls dispose even in derived classes. I achieve this by the following.
function close() {
closeCore();
dispose();
}
function closeCore() {
// derived class can override this method.
}
This works fine, but I have one case where I want to perform CSS animation before I dispose the instance. This is what I do.
function close () {
instance.classList.add("fancy-animation-that-takes-800ms");
setTimeout(function () {
dispose();
},800);
}
But as soon as I do this, the template pattern I use cannot be applied. Is there a way to make sure the close function always call dispose in the second example?
You might have close expect an object be returned from closeCore which had parameters like these:
return {timeout: 800, callback: function () {/*....will execute after a timeout..*/}};
or:
return {callback: function () { /*...will be immediately executed...*/}};
or:
return function () { /*...will be immediately executed...*/};
...and then call their timeout for them (if any), and then after your timeout executed their callback, then call dispose for them.
The relevant part of your close code might look like:
function close() {
var ccObj = closeCore();
var ccIsObj = ccObj && typeof ccObj === 'object';
var callback = typeof ccObj === 'function' ? ccObj : (ccIsObj ? ccObj.callback : null);
if (ccIsObj && ccObj.timeout) {
if (!callback) {
throw 'You must implement a callback when supplying a timeout';
}
setTimeout(function () {
callback();
dispose();
}, ccObj.timeout);
}
else {
if (callback) {callback();}
dispose();
}
}
But if you want to allow the user to make arbitrary asynchronous calls of their own (such as Ajax), while you could instead allow the returning of a promise to which you added the dispose call, you wouldn't have a guarantee that the deriver would ensure the promise completed. You could automatically cancel the promise after a certain period of time, but you couldn't magically know when the deriving code was meant to finish unless again you abstracted this.
I am trying to add some event listener to document, but for some reason it looks like the click event is never fired, because the callback is never called:
function NotJquery(element) {
this.element = element;
return this;
}
NotJquery.prototype.eventFired = function(event, callback) {
console.log('in NotJquery prototype');
return function (event, callback) {
element.addEventListener(event, callback, true);
};
};
var $ = function(element) {
return new NotJquery(element);
};
function Test() {
}
Test.prototype.addListener = function() {
console.log('in Test prototype');
$(document).eventFired('click', function() {
console.log('click event fired');
});
};
(function() {
var test= new Test();
test.addListener();
}());
Both the messages: "in Test prototype" and "in NotJquery prototype" are logged in the console, but when I click somewhere in my document the message "click event fired" is not output in the console. I do not see what is wrong with my code. Anybody has an idea to get it working?
http://jsfiddle.net/Nn3tZ/1/
Your client code is expecting something like this:
NotJquery.prototype.eventFired = function(event, callback) {
this.element.addEventListener(event, callback, false);
};
Working demo: http://jsfiddle.net/Nn3tZ/2/
element is not defined within your eventFired function (but that's not the only problem). Here's a minimal update:
NotJquery.prototype.eventFired = function(event, callback) {
var self = this; // <== Change 1 of 2
console.log('in NotJquery prototype');
return function () {
self.element.addEventListener(event, callback, true);
// ^-- Change 2 of 2
};
};
Unlike some other languages (Java, C#), this is not optional when referring to a property on the current object. Separately, the function you're creating within eventFired won't have the right this, so that's why I've stowed it away as self and then used self within the generated function (which is a closure).
Separately, you're passing event and callback into eventFired but then also declaring them on the generated function (it's not at all clear to me why you're generating a function there at all), so the ones you pass into eventFired are never used.
More reading (on my anemic blog):
Mythical Methods
You must remember this
Closures are not complicated