I have some javascript codes as following:
require([url0],f0);
require([url1],f1);
require([url2],f2);
require([url3],f3);
f0,f1,f2, and f3 are four functions.
Now I have another line of instruction. I want to execute it after all the four functions have finished execution. Where should I put the instruction, or should I change the structure of the existing codes?
You can create your own function that takes multiple arguments and calls a callback when they are all loaded:
function requireMulti(data, doneFn) {
var numRemaining = data.length;
data.forEach(function(val, index) {
require([val.url], function() {
--numRemaining;
val.fn.apply(this, arguments);
if (numRemaining === 0) {
doneFn();
}
});
});
}
// sample usage
requireMulti([
{url: url0, fn: f0},
{url: url1, fn: f1},
{url: url2, fn: f2},
{url: url3, fn: f3},
], function() {
// everything is done here
});
You may consider taking a counter which initial value is 4 and a callback, wrapped in a class.
function CountDown(c, f) {
this.counter = c;
this.func = f;
this.dec = function () {
this.counter--;
if (!this.counter) {
this.func();
}
}
}
var counter = new CountDown(4, function () { alert('got all 4'); });
In all functions of f0 ... f3, you can include a snippet like
function f0() {
// code
counter.dec();
}
Related
I want to call two function say function a() and function b() in parallel. These functions are independent to each other, and lets say the time required to execute these two functions are not fixed. Sometimes function a() will take more time than function b() and vice versa. But there is another function c() that should only execute when both the functions a() and b() are completed.
How should I do this using jQuery's Deferred object?
To achieve this you can make the a() and b() functions return deferred objects which you resolve() once their logic has completed. You can then run c() once both previous functions have completed. Try this:
function a() {
var aDef = $.Deferred();
setTimeout(function() {
aDef.resolve('a done');
}, 1000);
return aDef;
}
function b() {
var bDef = $.Deferred();
setTimeout(function() {
bDef.resolve('b done');
}, 3000);
return bDef;
}
function c() {
console.log('all done!')
}
console.log('running...');
$.when(a(), b()).done(function(a, b) {
console.log(a);
console.log(b);
c();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I'd use a global variable to determ an operational status and execute a polling each 100 milliseconds (or each milliseconds if you need).
var myStatus = {
"a": false,
"b": false
};
function a() {
myStatus["a"] = true;
console.log(myStatus['a']);
}
function b() {
myStatus["b"] = true;
}
function getStatusText() {
var s = 'not complete';
if (myStatus.a && myStatus.b) {
s = 'all complete';
} else {
if (myStatus.a) {
s = 'a complete';
}
if (myStatus.b) {
s = 'b complete';
}
}
return s;
}
function c() {
//check operational status
var statusText = getStatusText();
document.getElementById('status').innerHTML = statusText;
}
setInterval(
function() {
c()
}, 100);
<button onclick="a()">Set a() complete</button><button onclick="b()">Set b() complete</button>
<p>operational status <span id="status"></span></p>
Please refer Jquery defer and promise method to handle calls.
https://api.jquery.com/deferred.promise/ or
https://api.jquery.com/promise/
This is not exactly an answer to the question. I don't use defer or anything like it.
But I want to show something I do quite often: add a onReady callback, as a parameter to a() and b(). I add these callbacks to any self-written-function that takes time to execute.
function a(onready) {
// let's say we get Ajax data
$.ajax({
url: 'data.php',
success: function(data) {
$('#message').html(data);
if(typeof onready == 'function') {
onready(); // you might also want to add message as a parameter, like onready(data), or anready('Data okay'), ...
}
}
});
}
function b(onready) {
// let's say we sort <table> rows
sortTable('my_table', 'my_row', 'ASC'); // this function (not provided here) is not asynchronous, it just takes time before it's done
if(typeof onready == 'function') {
onready();
}
}
function c() {
alert('Yippy!');
}
$(document).ready(function() { // or this could be after the client clicks on a button, or so
var aReady = false;
var bReady = false;
a(function() {
aReady = true;
if(aReady && bReady) {
c();
}
});
b(function() {
bReady = true;
if(aReady && bReady) {
c();
}
});
});
You can use jQuery.when() to do this. Please read the document about this at https://api.jquery.com/jquery.when/
a = function () {
//your code for function a
}
b = function () {
//your code for function b
}
$.when( a(), b() ).done(function c() {
//your code for function c
});
Could you please tell me how do I write a function to check whether a specific function has been called, how many times it was called - with or without parameter.
Thanking you in advance
#elclanrs's solution is really good, but there are multiple problems with it:
You need to call the tracker function instead of the original function. That means you need to change some of your original code in order to use it.
You need to store a reference to the tracker object to get count.
Here is a solution for those problems:
function track() {
var calls = [],
context = window,
funcName,
i = 0;
if (arguments.length === 1) {
context = window;
funcName = arguments[0];
} else {
context = arguments[0];
funcName = arguments[1];
}
var func = context[funcName];
context[funcName] = function () {
calls.push({
count: i++,
args: Array.prototype.slice.call(arguments)
});
return func.apply(context, arguments);
};
context[funcName].getCalls = function () {
return calls;
};
}
Usage example:
// The function we want to track.
function log(val) {
console.log(val);
}
// Start tracking the function
track('log');
// Normal usage of the function
log('Message 1');
log('Message 2');
// Print the collected data of the function
console.log(log.getCalls());
/*^
[ { count: 0, args: [ 'Message 1' ] },
{ count: 1, args: [ 'Message 2' ] } ]
*/
Note: if your function is not in the global context (for example: document.getElementById), you need to do something like:
track(document, 'getElementById');
You can then collect the data normally:
document.getElementById.getCalls()
Each function has a caller property defined. You can make like this
function callfn() {
if (callfn.caller == null) {
return ("The function was called from the top!");
} else
return ("This function's caller was " + callfn.caller);
}
}
try something like this
var counter = {};
counter.with_param = 0;
counter.without_param = 0;
function test(arg){
if(!arg){
counter.without_param = counter.without_param + 1;
}else{
counter.with_param = counter.with_param + 1;
}
}
test(1);
test(5);
test();
test();
test(6);
console.log('WITH PARAM ' + counter.with_param);//3
console.log('WITHOUT PARAM ' + counter.without_param);//2
console.log('TOTAL CALLING ' + (counter.with_param + counter.without_param));//5
You could make a function decorator that saves the count and arguments in a closure, something like:
// Helper to convert pseudo-arrays
// such as `arguments` to real arrays
var __slice = Array.prototype.slice;
// Higher-order function that
// returns a function that saves the count
// and arguments when called
function track(f) {
var i = 0; // count
var calls = []; // array to save the info
var fn = function() {
// Save arguments and incremented counter
calls.push({
count: i++,
args: __slice.call(arguments)
});
// Call the original function with arguments
// preserving the context, and return its value
return f.apply(this, arguments);
};
// A function, attached to the decorated function,
// that gets the info out of the closure
fn.count = function() {
return calls;
};
// Return decorated function to be used
return fn;
}
// Decorate a normal function
var add1 = track(function(x) {
return x + 1;
});
// Run the function 3 times
// Internally the decorated function will keep
// track of the count and arguments
add1(1);
add1(2);
add1(3);
// Using the `count` function of our decorated function
console.log(add1.count());
/*^
[ { count: 0, args: [ 1 ] },
{ count: 1, args: [ 2 ] },
{ count: 2, args: [ 3 ] } ]
*/
This is probably not possible but maybe some of the stackoverflow geniuses can find a solution :)
W would like to have a function like this:
var myCrazyFunc;
myCrazyFunc = function (param1, callback) {
var funcId;
// I would like to get an Id of the function passed by callback
// which will be different for those two calls in example below
funcId = getFuncId(callback);
callback();
};
myCrazyFunc("param1", function () {
dosomething1;
});
myCrazyFunc("param1", function () {
dosomething2;
});
Please don't ask why I need that :) Simply it would simplify my code if that was possible.
Here is the function I made:
var myCrazyFunc;
var latestID = 0;
var funcToID = {};
function getFuncId(f) {
if (f in funcToID) {
return funcToID[f];
}
funcToID[f] = ++latestID;
return latestID;
}
myCrazyFunc = function(param1, callback) {
var funcId;
// I would like to get an Id of the function passed by callback
// which will be different for those two calls in example below
funcId = getFuncId(callback);
console.log(funcId);
callback();
};
myCrazyFunc("param1", function() {
'a';
});
myCrazyFunc("param1", function() {
'b';
});
this example would log:
1
2
I you run it with the same function code you get the same id, like here:
myCrazyFunc("param1", function() {
'a';
});
myCrazyFunc("param1", function() {
'a';
});
Ouput:
1
2
I hope that's ok.
enter code hereI have the following code
function a(){alert("a");}
I want to create a function b as
function b(){alert("a"); alert("b");}
My approach is something like
var b = a + alert("b");
This is of course not working. But I am wondering if there is some kind of library supporting this.
Edit: Maybe I need to describe my scenario so that its more clear what I want to achieve.
I am using async.js library to handler multiple async calls. My code looks like
var values = {};
...
function all() {
var allDfd = $.Deferred();
async.parallel(
[function (callback) {
remoteCall(function (result) {
values.v1 = result;
callback(null, 'one');
});
},
function (callback) {
remoteCall(function (result) {
values.v2 = result;
callback(null, "two");
});
},
function (callback) {
remoteCall(function (result) {
values.v3 = result;
callback(null, "three");
});
}], function (err, results) {
allDfd.resolve();
});
return allDfd.promise();
}
Clearly there are a lot of repetitive code that bothers me. So my idea is to create a function asyncCall to perform the boilerplate tasks. The idea is
var values = {};
...
function all() {
var allDfd = $.Deferred();
function getAsyncCall (func, innerCallback, callback) {
return function asyncCall(func, innnerCallback, callback){
func(innerCallback + callback(null)); // combine innerCallBack and callback code
}
}
async.parallel(
[getAsyncCall(remoteCall, function(result){values.v1=result;},callback),
getAsyncCall(remoteCall, function(result){values.v2=result;},callback),
getAsyncCall(remoteCall, function(result){values.v3=result;},callback),
], function (err, results) {
allDfd.resolve();
});
return allDfd.promise();
}
The line with the comment is what I am pondering. I am trying to create a new function that combines inner and outer callbacks.
You can do
var b = function() { a(); alert('b'); }
You could write:
var a=function(){alert("a");}
var b=function(){a(); alert("b");}
And to go a little further, you can even write a whole function composition function:
function compose( functions ) {
return function(){
for(var i=0; i!=functions.length; ++i) {
functions[i]();
}
};
}
var c=compose( [a, function(){ alert("b"); }] );
(See it at work at http://jsfiddle.net/xtofl/Pdrge/)
I have a web app which must call the server multiple times. So far, I had a long nested callback chain; but I would like to use jQuery's when,then etc. functionality. However, I can't seem to get stuff running again after using a then.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then ($.get('pages/test.html'))
.done (function(args)
{
// This prints the same as the last call
alert (args);
});
What am I doing wrong? I guess its some scoping issue, as I can see the second get call being executed. Using two different args variables does not help as the argument passed to the done function is still the first get request.
As an update:
With modern jquery (1.8+) you don't need the preliminary when because get returns a Deferred Promise.
Also, pipe is deprecated. Use then instead. Just be sure to return the result of the new get which becomes the Promise attached to by subsequent then/*done*/fail calls.
So:
$.get('pages/run-tool.html')
.then (function (args) { // this will run if the above .get succeeds
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.then (function() { // this will run after the above then-handler (assuming it ran)
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args) { // this will run after the second .get succeeds (assuming it ran)
alert (args);
});
All three callbacks (the two with then and the one with done) are applied to the same request – the original when call. This is because then returns the same Deferred object, rather than a new one, so that you can add multiple event handlers.
You need to use pipe instead.
$
.when ($.get('pages/run-tool.html'))
.then (function (args)
{
// This works fine
alert(args);
$('#content').replaceWith (args);
$('#progress-bar').progressbar ({value: 0});
})
.pipe (function() {
return $.get('pages/test.html'); // the return value creates a new Deferred object
})
.done (function(args)
{
alert (args);
});
Here is an wonderfully simple and highly effective AJAX chaining / queue plugin. It will execute you ajax methods in sequence one after each other.
It works by accepting an array of methods and then executing them in sequence. It wont execute the next method whilst waiting for a response.
//--- THIS PART IS YOUR CODE -----------------------
$(document).ready(function () {
var AjaxQ = [];
AjaxQ[0] = function () { AjaxMethod1(); }
AjaxQ[1] = function () { AjaxMethod2(); }
AjaxQ[3] = function () { AjaxMethod3(); }
//Execute methods in sequence
$(document).sc_ExecuteAjaxQ({ fx: AjaxQ });
});
//--- THIS PART IS THE AJAX PLUGIN -------------------
$.fn.sc_ExecuteAjaxQ = function (options) {
//? Executes a series of AJAX methods in dequence
var options = $.extend({
fx: [] //function1 () { }, function2 () { }, function3 () { }
}, options);
if (options.fx.length > 0) {
var i = 0;
$(this).unbind('ajaxComplete');
$(this).ajaxComplete(function () {
i++;
if (i < options.fx.length && (typeof options.fx[i] == "function")) { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
});
//Execute first item in queue
if (typeof options.fx[i] == "function") { options.fx[i](); }
else { $(this).unbind('ajaxComplete'); }
}
}
The answer cdr gave, which has the highest vote at the moment, is not right.
When you have functions a, b, c each returns a $.Deferred() object, and chains the functions like the following:
a().then(b).then(c)
Both b and c will run once the promise returned from a is resolved. Since both then() functions are tied to the promise of a, this works similiar to other Jquery chaining such as:
$('#id').html("<div>hello</div>").css({display:"block"})
where both html() and css() function are called on the object returned from $('#id');
So to make a, b, c run after the promise returned from the previous function is resolved, you need to do this:
a().then(function(){
b().then(c)
});
Here the call of function c is tied to the promise returned from function b.
You can test this using the following code:
function a() {
var promise = $.Deferred();
setTimeout(function() {
promise.resolve();
console.log("a");
}, 1000);
return promise;
}
function b() {
console.log("running b");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("b");
}, 500);
return promise;
}
function c() {
console.log("running c");
var promise = $.Deferred();
setTimeout(function () {
promise.resolve();
console.log("c");
}, 1500);
return promise;
}
a().then(b).then(c);
a().then(function(){
b().then(c)
});
Change the promise in function b() from resolve() to reject() and you will see the difference.
<script type="text/javascript">
var promise1 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("1");
def.resolve();
}, 3000);
}).promise();
};
var promise2 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("2");
def.resolve();
}, 2000);
}).promise();
};
var promise3 = function () {
return new
$.Deferred(function (def) {
setTimeout(function () {
console.log("3");
def.resolve();
}, 1000);
}).promise();
};
var firstCall = function () {
console.log("firstCall");
$.when(promise1())
.then(function () { secondCall(); });
};
var secondCall = function () {
console.log("secondCall")
$.when(promise2()).then(function () { thirdCall(); });
};
var thirdCall = function () {
console.log("thirdCall")
$.when(promise3()).then(function () { console.log("done"); });
};
$(document).ready(function () {
firstCall();
});
</script>
I thought I would leave this little exercise here for anyone who may find it useful, we build an array of requests and when they are completed, we can fire a callback function:
var urls = [{
url: 'url1',
data: 'foo'
}, {
url: 'url2',
data: 'foo'
}, {
url: 'url3',
data: 'foo'
}, {
url: 'url4',
data: 'foo'
}];
var requests = [];
var callback = function (result) {
console.log('done!');
};
var ajaxFunction = function () {
for (var request, i = -1; request = urls[++i];) {
requests.push($.ajax({
url: request.url,
success: function (response) {
console.log('success', response);
}
}));
}
};
// using $.when.apply() we can execute a function when all the requests
// in the array have completed
$.when.apply(new ajaxFunction(), requests).done(function (result) {
callback(result)
});
My way is to apply callback function:
A(function(){
B(function(){
C()})});
where A, B can be written as
function A(callback)
$.ajax{
...
success: function(result){
...
if (callback) callback();
}
}