I've really been having trouble grasping this concept and thought if I saw it on a little bit of my own code it might click. I'd really like to take advantage of callback functions while still keeping an object oriented approach. Thank you for any help you can offer!
//adds functionality to buttons
addClickEvent(newDataCollect,function() {addClickEvent(dataSubmitBtn, function(){testAjax(dataForm.elements);});
function addClickEvent(elem,click,addtl) {
var nwClickEvent = new elemEvents(elem,click,addtl);
nwClickEvent.onClick();
}
//add click event object & properties
function elemEvents(elem,click,addtl) {
this.elem = elem;
this.click = click;
this.addtl = addtl;
}
//add click event object method
elemEvents.prototype = {
onClick: function() {this.elem.onclick = this.click;}
}
Yes, I would say you are using a callback already:
//add click event object & properties (constructors should be PascalCase)
function ElemEvents(elem,click,addtl) {
this.elem = elem;
// `click` should be a function, and as such, a callback that is called when the element is clicked
this.elem.onclick = click;
this.addtl = addtl;
}
// later:
var e = new ElemEvents(
// the `elem`
document.getElementById("id"),
// this is the callback.
function (event) {
console.log("hi from event: " + event);
},
// the `addtl`.
"");
One thing to keep in mind here is that callbacks are not necessarily asynchronous. In this particular case, you are defining an onClick handler which will run once the user clicks on something, at some point in the future. This is async - but consider the following:
function log(/* .. */){ // can be any arguments, we're just .apply'ing log with arguments
console.log.apply(console, arguments)
}
function logThen(done /* .. */) { // takes a callback as first argument, then vals to log
var args = [].splice.call(arguments, 1); // calls splice on the arguments, removing the CB, and returning whatever you had left
console.log.apply(console, args);
if (done && typeof done === 'function'){
done();
}
}
log(1);
log(2);
log(3);
logThen(alert, 4);
log(5);
log(6);
The output will be:
1
2
3
4 // shows an alert, then continues with..
5
6
This is all synchronous code - there is nothing that is pushed onto the next call stack, so it executes everything, including your callback (in this case alert) synchronously.
Just a little heads up!
Related
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 have three functions that I want to run in sequence (and then repeat, but I'm not even on that yet.) So when the first function displays its content and then leaves, the second function will play afterwards and do the same thing. Then that repeats into the third function. I'm using callbacks to try to achieve this.
This isn't a problem when I'm using only two functions, but when I introduce the third, It renders the first two menu boards, and then the third one comes afterwards, when they should render 1, 2 and then 3.
JavaScript for Reference
$(document).ready(function(){
Board1 = function(callback){
$('#menu-board .board.one .row').slideDown(800).delay(10000).slideUp(800, function(){
callback();
});
}
Board2 = function(callback){
$('#menu-board .board.two .row').slideDown(800).delay(10000).slideUp(800, function(){
callback();
});
}
Board3 = function(){
$('#menu-board .board.three .row').slideDown(800).delay(10000).slideUp(800);
}
Board1(Board2(Board3));
});
Any help is appreciated. Thank you.
Board1(Board2(Board3));
is equal to:
var res = Board2(Board3);
Board1(res);
So it won't act as you expect, it just start to execute Board2, and then start Board1, so Board3 is only guranteed to execute after Board2, while the order of Board1 is not relevant to Board2 and Board3.
You can use .bind to create a function that calls Board2 with give param Board3 like:
Board1(Board2.bind(null, Board3));
or just wrap them in another function:
Board1(function() {
Board2(Board3);
});
However, if you have too many functions to chain, use the methods above may not be a good idea, then you may create a chainer to do what you want:
// This function will accept a sequnce of functions in array, execute them in order, and call the done callback when all is complete.
var chain = function(sequences, done) {
// Manage the current index, and total items that would be called.
var idx = 0, length = sequences.length;
var caller = function() {
// When all functions in sequence is called, call final callback to notify user
// you may have to check if done is a function or not.
if (idx === length) {
if (typeof done === 'function') {
done();
}
return;
}
// Get the next function to call.
var currentTarget = sequences[idx];
// Pass caller to the target function, so when the function completes and call the callback
// the caller can takeover and start to call next function in sequence.
currentTarget(caller);
++idx;
};
caller();
};
// Create some test cases.
var sequence = [], i;
for (i = 0; i < 10; ++i) {
// Create some functions that will display some text after 1 sec when it get called.
sequence[i] = (function(index) {
return function(cb) {
setTimeout(function() {
var div = document.createElement('div');
div.innerHTML = 'Index is: ' + index;
document.body.appendChild(div);
cb();
}, 1000);
};
}(i));
}
// Demo.
chain(sequence, function() {
document.body.appendChild(document.createTextNode("All done."));
});
By the chain function above, you can now use it as chain([Board1, Board2, Board3]) and it keeps the codes simple even if you have a sequence of many functions.
PLUS:
From .slideUp()'s document:
Callback Function
If supplied, the callback is fired once the animation is complete.
This can be useful for stringing different animations together in
sequence. The callback is not sent any arguments, but this is set to
the DOM element being animated. If multiple elements are animated, it
is important to note that the callback is executed once per matched
element, not once for the animation as a whole.
As of jQuery 1.6, the .promise() method can be used in conjunction
with the deferred.done() method to execute a single callback for the
animation as a whole when all matching elements have completed their
animations ( See the example for .promise() ).
So if there's more than 1 element match to animate, the callback in your current function will get called more than once, you may have to rewrite your function with what the doc suggest to
Board1 = function(callback){
$('#menu-board .board.one .row').slideDown(800).delay(1000).slideUp(800).promise().done(callback);
}
You can see the jsfiddle that work as you expect.
why dont you just call the callback function directly in the slideup function.somewhat like this:
$('#menu-board .board.one .row').slideDown(800).delay(10000).slideUp(800, callback);
let me know if this does not work.
This is the reference for slideup function:
http://api.jquery.com/slideup/
I have to call up a function (checkImdb) that will fetch some info from a php file (temp.php) and put some contents on a div (placeToFetchTo). This has to be done a certain amount of times, so I used a FOR LOOP for that.
The problem is that only the last instance of the looped counter (currentCastId) gets used. I understand there needs to be a way to force the FOR LOOP to wait for the fetch to be complete, and I have been looking online for answers but nothing seems to work so far. I apologise if I have missed an eventual answer that already exists.
Any help is appreciated.
This is the code I am referring to:
function checkImdb (totalCasts) {
$(function() {
for (currentCastId = 1; currentCastId <= totalCasts; currentCastId++) {
//Gets cast IMDB#
var row = document.getElementById("area2-" + currentCastId)
row = row.innerHTML.toString();
var fetchThis = "temp.php?id=" + row + "\ .filmo-category-section:first b a";
placeToFetchTo = "#area0-" + currentCastId;
function load_complete() {
var filhos = $(placeToFetchTo).children().length, newDiv ="";
var nrMoviesMissing = 0, looped = 0;
alert("done- "+ placeToFetchTo);
}
document.getElementById("area0").innerHTML = document.getElementById("area0").innerHTML + "<div id=\"area0-" + currentCastId + "\"></div>";
$(placeToFetchTo).load(fetchThis, null, load_complete);
} //End of: for (imdbLooper = 0; imdbLooper <= totalCasts; imdbLooper++) {
}); //End of: $(function() {
}
2017 update: The original answer had the callback arg as last arg in the function signature. However, now that the ES6 spread operator is a real thing, best practice is to put it first, not last, so that the spread operator can be used to capture "everything else".
You don't really want to use a for loop if you need to do any "waiting". Instead, use self-terminating recursion:
/**
* This is your async function that "does things" like
* calling a php file on the server through GET/POST and
* then deals with the data it gets back. After it's done,
* it calls the function that was passed as "callback" argument.
*/
function doAsynchronousStuff(callback, ...) {
//... your code goes here ...
// as final step, on the "next clock tick",
// call the "callback" function. This makes
// it a "new" call, giving the JS engine some
// time to slip in other important operations
// in its thread. This basically "unblocks"
// JS execution.
requestAnimationFrame(function() {
callback(/* with whatever args it needs */);
});
}
/**
* This is your "control" function, responsible
* for calling your actual worker function as
* many times as necessary. We give it a number that
* tells it how many times it should run, and a function
* handle that tells it what to call when it has done
* all its iterations.
*/
function runSeveralTimes(fnToCallWhenDone, howManyTimes) {
// if there are 0 times left to run, we don't run
// the operation code, but instead call the "We are done"
// function that was passed as second argument.
if (howManyTimes === 0) {
return fnToCallWhenDone();
}
// If we haven't returned, then howManyTimes is not
// zero. Run the real operational code once, and tell
// to run this control function when its code is done:
doAsynchronousStuff(function doThisWhenDone() {
// the "when done with the real code" function simply
// calls this control function with the "how many times?"
// value decremented by one. If we had to run 5 times,
// the next call will tell it to run 4 times, etc.
runSeveralTimes(fnToCallWhenDone, howManyTimes - 1);
}, ...);
}
In this code the doAsynchronousStuff function is your actual code.
The use of requestAnimationFrame is to ensure the call doesn't flood the callstack. Since the work is technically independent, we can schedule it to be called "on the next tick" instead.
The call chain is a bit like this:
// let's say we need to run 5 times
runSeveralTimes(5);
=> doAsynchronousStuff()
=> runSeveralTimes(5-1 = 4)
=> this is on a new tick, on a new stack, so
this actually happens as if a "new" call:
runSeveralTimes(4)
=> doAsynchronousStuff()
=> runSeveralTimes(4-1 = 3), on new stack
runSeveralTimes(3)
...
=> doAsynchronousStuff()
=> runSeveralTimes(1-1 = 0), on new stack
runSeveralTimes(0)
=> fnToCallWhenDone()
=> return
<end of call chain>
You need to use a while loop and have the loop exit only when all your fetches have completed.
function checkImdb (totalCasts) {
currentCastId = 1;
totalCasts = 3;
doneLoading = false;
while (!doneLoading)
{
//do something
currentCastId++;
if (currentCastId == totalCasts)
doneLoading = true;
}
}
I found a bug, and tracked it down.
You can see a simplified example of my code here.
As it turns out, I need to debounce my if() statement rather than debouncing the function itself.
I'd like to keep the debounce as a standalone function, but I'm not sure then how to pass the conditional in.
Any pointers?
Here's the code:
var foo = function(xyz) {
alert(xyz);
};
function setter(func, arg1, arg2) {
return {
fn: func,
arg1: arg1,
arg2: arg2
};
}
function debounce(someObject) {
var duration = someObject.arg2 || 100;
var timer;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
someObject.fn(someObject.arg1);
timer = 0;
}, duration);
}
var toggle = true;
if (toggle) {
debounce(setter(foo, 'The best things in life are worth waiting for.', 1250));
} else {
foo('Instant gratification is sweet!!');
}
Using your example, why not pass toggle in as arg 1... something like:
var toggle = true;
var debouncedFunk = function(toggle) {
if (toggle)
// the function call
else
// something else
};
debounce(debouncedFunk, toggle, 1250);
You should also look into using the Function objects .call and .apply methods. They are for calling the function and passing in arguments. Taking the example function:
var example = function(one, two) {
// Logic here
};
You can call it in three ways:
// First
example(1, 2);
// Second
example.call({}, 1, 2);
// Third
example.apply({}, [ 1, 2 ]);
The first is the standard way to call a function. The difference between the first and the .call is that the first parameter to .call is the context object of the function (what this will point to inside the function), the other parameters are passed after that (and a known list is required for .call. The benefit of .apply is that you can pass an array to the function of arguments and they will be assigned to the parameter list appropriately, the first parameter is still the context object.
It would simplify your debounce function, instead of having to deal with a structured object as you currently do.
A suggestion for your debounce:
var debounce = function(funk, delay) {
var args = [];
if (arguments.length > 2)
args = [].slice.call(arguments, 2);
setTimeout(function() { funk.apply({}, args); }, delay);
};
Changing your current if to:
var toggle = true;
var debouncedFunk = function(toggle) {
if (toggle)
// Do if true
else
// DO if false
};
debounce(debouncedFunk, 1000, toggle);
Maybe too much information (sorry)?
As a last note, I'd recommend using a framework (if possible) where these functions have been implemented already (and many other useful functions) such as Underscore. Using Underscore your example would look like:
// Define debouncedFunk and toggle
debouncedFunk = _.bind(debouncedFunk, {}, toggle);
debouncedFunk = _.debounce(debouncedFunk, 1000);
debouncedFunk();
EDIT
Fixed the underscore example, _.debounce returns a function that will execute only after the delay but it still needs to be called.
This question already has answers here:
Semaphore-like queue in javascript?
(4 answers)
Closed 6 years ago.
I have created a Queue class in javascript and I would like to store functions as data in a queue. That way I can build up requests (function calls) and respond to them when I need to (actually executing the function).
Is there any way to store a function as data, somewhat similar to
.setTimeout("doSomething()", 1000);
except it would be
functionQueue.enqueue(doSomething());
Where it would store doSomething() as data so when I retrieve the data from the queue, the function would be executed.
I'm guessing I would have to have doSomething() in quotes -> "doSomething()" and some how make it call the function using a string, anyone know how that could be done?
All functions are actually variables, so it's actually pretty easy to store all your functions in array (by referencing them without the ()):
// Create your functions, in a variety of manners...
// (The second method is preferable, but I show the first for reference.)
function fun1() { alert("Message 1"); };
var fun2 = function() { alert("Message 2"); };
// Create an array and append your functions to them
var funqueue = [];
funqueue.push(fun1);
funqueue.push(fun2);
// Remove and execute the first function on the queue
(funqueue.shift())();
This becomes a bit more complex if you want to pass parameters to your functions, but once you've setup the framework for doing this once it becomes easy every time thereafter. Essentially what you're going to do is create a wrapper function which, when invoked, fires off a predefined function with a particular context and parameter set:
// Function wrapping code.
// fn - reference to function.
// context - what you want "this" to be.
// params - array of parameters to pass to function.
var wrapFunction = function(fn, context, params) {
return function() {
fn.apply(context, params);
};
}
Now that we've got a utility function for wrapping, let's see how it's used to create future invocations of functions:
// Create my function to be wrapped
var sayStuff = function(str) {
alert(str);
}
// Wrap the function. Make sure that the params are an array.
var fun1 = wrapFunction(sayStuff, this, ["Hello, world!"]);
var fun2 = wrapFunction(sayStuff, this, ["Goodbye, cruel world!"]);
// Create an array and append your functions to them
var funqueue = [];
funqueue.push(fun1);
funqueue.push(fun2);
// Remove and execute all items in the array
while (funqueue.length > 0) {
(funqueue.shift())();
}
This code could be improved by allowing the wrapper to either use an array or a series of arguments (but doing so would muddle up the example I'm trying to make).
Canonical answer posted here
Here is a nice Queue class you can use without the use of timeouts:
var Queue = (function(){
function Queue() {};
Queue.prototype.running = false;
Queue.prototype.queue = [];
Queue.prototype.add_function = function(callback) {
var _this = this;
//add callback to the queue
this.queue.push(function(){
var finished = callback();
if(typeof finished === "undefined" || finished) {
// if callback returns `false`, then you have to
// call `next` somewhere in the callback
_this.next();
}
});
if(!this.running) {
// if nothing is running, then start the engines!
this.next();
}
return this; // for chaining fun!
}
Queue.prototype.next = function(){
this.running = false;
//get the first element off the queue
var shift = this.queue.shift();
if(shift) {
this.running = true;
shift();
}
}
return Queue;
})();
It can be used like so:
var queue = new Queue;
queue.add_function(function(){
//start running something
});
queue.add_function(function(){
//start running something 2
});
queue.add_function(function(){
//start running something 3
});
Refer to the function you're storing without the () at the end. doSomething is a variable (that happens to be a function); doSomething() is an instruction to execute the function.
Later on, when you're using the queue, you'll want something like (functionQueue.pop())() -- that is, execute functionQueue.pop, and then execute the return value of that call to pop.
You can also use the .call() method of a function object.
function doSomething() {
alert('doSomething');
}
var funcs = new Array();
funcs['doSomething'] = doSomething;
funcs['doSomething'].call();
In addition, you can also add the function directly to the queue:
funcs['somethingElse'] = function() {
alert('somethingElse');
};
funcs['somethingElse'].call();