Jquery $.when blocks code execution and UI updates? - javascript

We are using JQuery in our single page application.
Our boostrapper is the starting point for the app and looks like this:
define('homebootstrapper',
['jquery', 'config', 'homerouteconfig', 'presenter', 'dataprimer', 'binder'],
function ($, config, homeRouteConfig, presenter, dataprimer, binder) {
var
run = function () {
$('#busyIndicator').activity(true);
$.when(dataprimer.fetch())
.done(function () {
// $('#busyIndicator').activity(false);
});
};
return {
run: run
};
});
The dataprimer being called looks like this:
define('dataprimer',
['ko', 'datacontext', 'config'],
function (ko, datacontext, config) {
var logger = config.logger,
fetch = function () {
return $.Deferred(function (def) {
console.log('in deferred');
$.when(LongTimeProcessing())
.pipe(function () {
logger.success('Fetched data');
})
.fail(function () { def.reject(); })
.done(function () { def.resolve(); });
}).promise();
};
return {
fetch: fetch
};
});
function LongTimeProcessing(options) {
console.log('in when');
return $.Deferred(function (def) {
var results = options;
for (var i = 0; i < 10; i++) {
var x = i;
$('#counter').html(x);
}
def.resolve(results);
}).promise();
}
The line $('#busyIndicator').activity(true);
should display a progress animation based on SVG or VML. This works well, except when using the JQuery $.when
With this sample code we've tried to create a method that will take some time and is called 'LongTimeProcessing' (instead of an ajax call to the backend which is normally used through amplify)
We see that when we use jquery, the busyindicator doesn't work (read: is not displayed) until the dataprimer returns with def.resolve(). This when seems to block all UI updates. Also, the counter value from the LongTimeProcessing method only shows the last value from this loop. It is executed but it is never visible.
What are we doing wrong? How should we handle this.

You MUST yield to the event processing loop to allow the UI to update and to process outstanding events. This only happens when your own code has finished executing.
Your LongTimeProcessing function does not do this, it starts a loop and doesn't return control to the browser until that loop has finished.
You could achieve what you want by using setTimeout to handle the loop iteration:
function LongTimeProcessing(options) {
console.log('in when');
return $.Deferred(function (def) {
var results = options;
var i = 0;
(function iterate() {
$('#counter').html(i);
if (i < 10) {
++i;
setTimeout(iterate, 250);
} else {
def.resolve(results);
}
})();
}).promise();
}
The call to setTimeout allows the function to terminate immediately after the first call to iterate(), allowing the browser's event processing loop to process UI updates, and then going into the next iteration when the timer elapses.

Related

setInterval fires Ajax call twice every time

I have the following JS code:
var delay = 5000;
function init() {
setInterval(getFileCount, delay);
}
function getFileCount() {
$.get('/notification/course-file-count', function(response) {
if (response.items.length === 0) {
return false;
}
// Do stuff with response
});
}
On page load I'm calling the init() function. The idea is to start the interval and call the getFileCount() function every 5 seconds.
So, the interval waits 5s after the page loads and runs, but it always makes the Ajax call twice.
What am I missing?
UPDATE:
I know the init() function is triggered twice on page load (thanks to the comment by Yury Tarabanko). I don't quite understand, why. The almost-full code:
$(function() {
'use strict';
function handleCourseNotification() {
var delay = 5000;
function init() {
setInterval(getFileCount, delay);
}
function getFileCount() {
$.get('/notification/course-file-count', function(response) {
if (response.items.length === 0) {
return false;
}
updateCourseList(response.items);
});
}
function updateCourseList(items) {
// update course list...
}
return {
init: init
};
}
if ($('#js-auth-course-list').length) {
var notificationHandler = handleCourseNotification();
notificationHandler.init();
}
});
It's a small module, which I initialize after page load, if a specific element is available in the DOM - $('#js-auth-course-list'). Why is init called 2 times actually? I directly call it once.
In general, it is not a good idea to call asynchronous calls inside a setInterval() because you do not know the exact response time. So, you could end up calling the second async function before the response from the first call has returned.
You can try with setTimeout() like this:
var delay = 5000;
var async = function() {
$.get('/notification/course-file-count', function(response) {
if (response.items.length === 0) {
return false;
}
// Do stuff with response
// Call the async function again
setTimeout(function() {
async();
}, delay);
});
}
async();

jQuery: using deferred properly in a local context (ie. no AJAX)

Apologies for what I'm sure is a repost; I really have looked quite widely for an answer to my question (that I also understood).
What I'm trying to learn to do is to arbitrarily chain functions such that they must complete before the next occurs, which, as I understand it, is the purpose of jQuery's deferred(). So in the below code, what I'm imagining should happen is:
the function contained within the load deferred objects executes; after which
the function contained in then() executes; after which
the function contained in done() executes.
Every tutorial in the universe uses a $.ajax() object after $.when(), which is useless if all one wants is control of execution sequence in a local context.
Here's what I've been trying:
var preloadDone = false,
var testDone = false,
var load = $.deferred(function() {
//cacheImages() is a plugin, works fine
$("img.image-loader.preload").cacheImages();
preloadDone = true;
});
var loader = $.when(load)
.then(function() {
if (preloadDone) {
console.log("then called in sequence");
} else {
console.log("then called out of sequence"); // wrong order, every time
}
XBS.cache.cbDone = true;
}).done(function() {
if (XBS.cache.cbDone) {
console.log("even done was called in right sequence!"); // proper order, every time
} else {
console.log("done() wasn't called in order..");
}
});
load.resolve(); // nothing happens
// load(); also tried this; nothing happens
So far as I can tell, this is identical to the example given in the jQuery $.when() documentation. Lil help?
Here is a demo on how to run many functions one after another but only after each funtion has completed. This is achieved by using an Async function.
Demo (Runs 3 functions one after the other. Where i have alert("starting *") that is were you want to put the work you like to do and in the done function you include the next function you want to run. )
http://jsfiddle.net/5xLbk91c/
//the Assync function. Pause is the time in miliseconds to pause between loops
var asyncFor = function(params) {
var defaults = {
total: 0,
limit: 1,
pause: 10,
context: this
},
options = $.extend(defaults, params),
def = $.Deferred(),
step = 0,
done = 0;
this.loop = function() {
if (done < options.total) {
step = 0;
for (; step < options.limit; step += 1, done += 1) {
def.notifyWith(options.context, [done]);
}
setTimeout.apply(this, [this.loop, options.pause]);
} else {
def.resolveWith(options.context);
}
};
setTimeout.apply(this, [this.loop, options.pause]);
return def;
};
function one() {
asyncFor({
total: 1, // run only once. If you want to loop then increase to desired total.
context: this
}).progress(function(step) {
alert("starting one")
}).done(function() {
alert("finished one")
two()
});
}
function two() {
asyncFor({
total: 1,
context: this
}).progress(function(step) {
alert("starting two")
}).done(function() {
alert("finished two")
three()
});
}
function three() {
asyncFor({
total: 1,
context: this
}).progress(function(step) {
alert("starting three")
}).done(function() {
alert("finished three and all done")
});
}
you may want to start your investigations by this change to your code:
var load = function() {
var deferred = $.Deferred();
$("img.image-loader.preload").cacheImages();
preloadDone = true;
return deferred;
};
Please also note you may pass array of promises to $.when().
Best regards

Wait till a Function with animations is finished until running another Function

I'm having an issue with normal (non-ajax) functions that involve lots of animations within each of them. Currently I simply have a setTimeout between functions, but this isn't perfect since no browsers / computers are the same.
Additional Note: They both have separate animations/etc that collide.
I can't simply put one in the callback function of another
// multiple dom animations / etc
FunctionOne();
// What I -was- doing to wait till running the next function filled
// with animations, etc
setTimeout(function () {
FunctionTwo(); // other dom animations (some triggering on previous ones)
}, 1000);
Is there anyway in js/jQuery to have:
// Pseudo-code
-do FunctionOne()
-when finished :: run -> FunctionTwo()
I know about $.when() & $.done(), but those are for AJAX...
MY UPDATED SOLUTION
jQuery has an exposed variable (that for some reason isn't listed anywhere in the jQuery docs) called $.timers, which holds the array of animations currently taking place.
function animationsTest (callback) {
// Test if ANY/ALL page animations are currently active
var testAnimationInterval = setInterval(function () {
if (! $.timers.length) { // any page animations finished
clearInterval(testAnimationInterval);
callback();
}
}, 25);
};
Basic useage:
// run some function with animations etc
functionWithAnimations();
animationsTest(function () { // <-- this will run once all the above animations are finished
// your callback (things to do after all animations are done)
runNextAnimations();
});
You can use jQuery's $.Deferred
var FunctionOne = function () {
// create a deferred object
var r = $.Deferred();
// do whatever you want (e.g. ajax/animations other asyc tasks)
setTimeout(function () {
// and call `resolve` on the deferred object, once you're done
r.resolve();
}, 2500);
// return the deferred object
return r;
};
// define FunctionTwo as needed
var FunctionTwo = function () {
console.log('FunctionTwo');
};
// call FunctionOne and use the `done` method
// with `FunctionTwo` as it's parameter
FunctionOne().done(FunctionTwo);
you could also pack multiple deferreds together:
var FunctionOne = function () {
var
a = $.Deferred(),
b = $.Deferred();
// some fake asyc task
setTimeout(function () {
console.log('a done');
a.resolve();
}, Math.random() * 4000);
// some other fake asyc task
setTimeout(function () {
console.log('b done');
b.resolve();
}, Math.random() * 4000);
return $.Deferred(function (def) {
$.when(a, b).done(function () {
def.resolve();
});
});
};
http://jsfiddle.net/p22dK/
add the following to the end of the first function
return $.Deferred().resolve();
call both functions like so
functionOne().done(functionTwo);
Along with Yoshi's answer, I have found another very simple (callback type) solution for animations.
jQuery has an exposed variable (that for some reason isn't listed anywhere in the jQuery docs) called $.timers, which holds the array of animations currently taking place.
function animationsTest (callback) {
// Test if ANY/ALL page animations are currently active
var testAnimationInterval = setInterval(function () {
if (! $.timers.length) { // any page animations finished
clearInterval(testAnimationInterval);
callback();
}
}, 25);
};
Basic useage:
functionOne(); // one with animations
animationsTest(functionTwo);
Hope this helps some people out!
This answer uses promises, a JavaScript feature of the ECMAScript 6 standard. If your target platform does not support promises, polyfill it with PromiseJs.
You can get the Deferred object jQuery creates for the animation using .promise() on the animation call. Wrapping these Deferreds into ES6 Promises results in much cleaner code than using timers.
You can also use Deferreds directly, but this is generally discouraged because they do not follow the Promises/A+ specification.
The resulting code would look like this:
var p1 = Promise.resolve($('#Content').animate({ opacity: 0.5 }, { duration: 500, queue: false }).promise());
var p2 = Promise.resolve($('#Content').animate({ marginLeft: "-100px" }, { duration: 2000, queue: false }).promise());
Promise.all([p1, p2]).then(function () {
return $('#Content').animate({ width: 0 }, { duration: 500, queue: false }).promise();
});
Note that the function in Promise.all() returns the promise. This is where magic happens. If in a then call a promise is returned, the next then call will wait for that promise to be resolved before executing.
jQuery uses an animation queue for each element. So animations on the same element are executed synchronously. In this case you wouldn't have to use promises at all!
I have disabled the jQuery animation queue to demonstrate how it would work with promises.
Promise.all() takes an array of promises and creates a new Promise that finishes after all promises in the array finished.
Promise.race() also takes an array of promises, but finishes as soon as the first Promise finished.
Is this what you mean man: http://jsfiddle.net/LF75a/
You will have one function fire the next function and so on, i.e. add another function call and then add your functionONe at the bottom of it.
Please lemme know if I missed anything, hope it fits the cause :)
or this: Call a function after previous function is complete
Code:
function hulk()
{
// do some stuff...
}
function simpsons()
{
// do some stuff...
hulk();
}
function thor()
{
// do some stuff...
simpsons();
}
ECMAScript 6 UPDATE
This uses a new feature of JavaScript called Promises
functionOne().then(functionTwo);
You can do it via callback function.
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable, function() {
function2(someOtherVariable);
});
}
else {
doThis(someVariable);
}
});
function function1(param, callback) {
...do stuff
callback();
}
Here is a solution for n-calls (recursive function).
https://jsfiddle.net/mathew11/5f3mu0f4/7/
function myFunction(array){
var r = $.Deferred();
if(array.length == 0){
r.resolve();
return r;
}
var element = array.shift();
// async task
timer = setTimeout(function(){
$("a").text($("a").text()+ " " + element);
var resolving = function(){
r.resolve();
}
myFunction(array).done(resolving);
}, 500);
return r;
}
//Starting the function
var myArray = ["Hi", "that's", "just", "a", "test"];
var alerting = function (){window.alert("finished!")};
myFunction(myArray).done(alerting);
You can use the javascript Promise and async/await to implement a synchronized call of the functions.
Suppose you want to execute n number of functions in a synchronized manner that are stored in an array, here is my solution for that.
async function executeActionQueue(funArray) {
var length = funArray.length;
for(var i = 0; i < length; i++) {
await executeFun(funArray[i]);
}
};
function executeFun(fun) {
return new Promise((resolve, reject) => {
// Execute required function here
fun()
.then((data) => {
// do required with data
resolve(true);
})
.catch((error) => {
// handle error
resolve(true);
});
})
};
executeActionQueue(funArray);

How can I create an Asynchronous function in Javascript?

Check out this code :
Link
<span>Moving</span>
$('#link').click(function () {
console.log("Enter");
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
});
console.log("Exit");
});
As you can see in the console, the "animate" function is asynchronous, and it "fork"s the flow of the event handler block code. In fact :
$('#link').click(function () {
console.log("Enter");
asyncFunct();
console.log("Exit");
});
function asyncFunct() {
console.log("finished");
}
follow the flow of the block code!
If I wish to create my function asyncFunct() { } with this behaviour, how can I do it with javascript/jquery? I think there is a strategy without the use of setTimeout()
​
You cannot make a truly custom asynchronous function. You'll eventually have to leverage on a technology provided natively, such as:
setInterval
setTimeout
requestAnimationFrame
XMLHttpRequest
WebSocket
Worker
Some HTML5 APIs such as the File API, Web Database API
Technologies that support onload
... many others
In fact, for the animation jQuery uses setInterval.
You can use a timer:
setTimeout( yourFn, 0 );
(where yourFn is a reference to your function)
or, with Lodash:
_.defer( yourFn );
Defers invoking the func until the current call stack has cleared. Any additional arguments are provided to func when it's invoked.
here you have simple solution (other write about it)
http://www.benlesh.com/2012/05/calling-javascript-function.html
And here you have above ready solution:
function async(your_function, callback) {
setTimeout(function() {
your_function();
if (callback) {callback();}
}, 0);
}
TEST 1 (may output '1 x 2 3' or '1 2 x 3' or '1 2 3 x'):
console.log(1);
async(function() {console.log('x')}, null);
console.log(2);
console.log(3);
TEST 2 (will always output 'x 1'):
async(function() {console.log('x');}, function() {console.log(1);});
This function is executed with timeout 0 - it will simulate asynchronous task
Here is a function that takes in another function and outputs a version that runs async.
var async = function (func) {
return function () {
var args = arguments;
setTimeout(function () {
func.apply(this, args);
}, 0);
};
};
It is used as a simple way to make an async function:
var anyncFunction = async(function (callback) {
doSomething();
callback();
});
This is different from #fider's answer because the function itself has its own structure (no callback added on, it's already in the function) and also because it creates a new function that can be used.
Edit: I totally misunderstood the question. In the browser, I would use setTimeout. If it was important that it ran in another thread, I would use Web Workers.
Late, but to show an easy solution using promises after their introduction in ES6, it handles asynchronous calls a lot easier:
You set the asynchronous code in a new promise:
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
Note to set resolve() when async call finishes.
Then you add the code that you want to run after async call finishes inside .then() of the promise:
asyncFunct.then((result) => {
console.log("Exit");
});
Here is a snippet of it:
$('#link').click(function () {
console.log("Enter");
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
asyncFunct.then((result) => {
console.log("Exit");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Link
<span>Moving</span>
or JSFiddle
This page walks you through the basics of creating an async javascript function.
Since ES2017, asynchronous javacript functions are much easier to write. You should also read more on Promises.
If you want to use Parameters and regulate the maximum number of async functions you can use a simple async worker I've build:
var BackgroundWorker = function(maxTasks) {
this.maxTasks = maxTasks || 100;
this.runningTasks = 0;
this.taskQueue = [];
};
/* runs an async task */
BackgroundWorker.prototype.runTask = function(task, delay, params) {
var self = this;
if(self.runningTasks >= self.maxTasks) {
self.taskQueue.push({ task: task, delay: delay, params: params});
} else {
self.runningTasks += 1;
var runnable = function(params) {
try {
task(params);
} catch(err) {
console.log(err);
}
self.taskCompleted();
}
// this approach uses current standards:
setTimeout(runnable, delay, params);
}
}
BackgroundWorker.prototype.taskCompleted = function() {
this.runningTasks -= 1;
// are any tasks waiting in queue?
if(this.taskQueue.length > 0) {
// it seems so! let's run it x)
var taskInfo = this.taskQueue.splice(0, 1)[0];
this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params);
}
}
You can use it like this:
var myFunction = function() {
...
}
var myFunctionB = function() {
...
}
var myParams = { name: "John" };
var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);
Function.prototype.applyAsync = function(params, cb){
var function_context = this;
setTimeout(function(){
var val = function_context.apply(undefined, params);
if(cb) cb(val);
}, 0);
}
// usage
var double = function(n){return 2*n;};
var display = function(){console.log(arguments); return undefined;};
double.applyAsync([3], display);
Although not fundamentally different than the other solutions, I think my solution does a few additional nice things:
it allows for parameters to the functions
it passes the output of the function to the callback
it is added to Function.prototype allowing a nicer way to call it
Also, the similarity to the built-in function Function.prototype.apply seems appropriate to me.
Next to the great answer by #pimvdb, and just in case you where wondering, async.js does not offer truly asynchronous functions either. Here is a (very) stripped down version of the library's main method:
function asyncify(func) { // signature: func(array)
return function (array, callback) {
var result;
try {
result = func.apply(this, array);
} catch (e) {
return callback(e);
}
/* code ommited in case func returns a promise */
callback(null, result);
};
}
So the function protects from errors and gracefully hands it to the callback to handle, but the code is as synchronous as any other JS function.
Unfortunately, JavaScript doesn't provide an async functionality. It works only in a single one thread. But the most of the modern browsers provide Workers, that are second scripts which gets executed in background and can return a result.
So, I reached a solution I think it's useful to asynchronously run a function, which creates a worker for each async call.
The code below contains the function async to call in background.
Function.prototype.async = function(callback) {
let blob = new Blob([ "self.addEventListener('message', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" });
let worker = new Worker(window.URL.createObjectURL(blob));
worker.addEventListener("message", function(e) {
this(e.data.result);
}.bind(callback), false);
return function() {
this.postMessage(Array.from(arguments));
}.bind(worker);
};
This is an example for usage:
(function(x) {
for (let i = 0; i < 999999999; i++) {}
return x * 2;
}).async(function(result) {
alert(result);
})(10);
This executes a function which iterate a for with a huge number to take time as demonstration of asynchronicity, and then gets the double of the passed number.
The async method provides a function which calls the wanted function in background, and in that which is provided as parameter of async callbacks the return in its unique parameter.
So in the callback function I alert the result.
MDN has a good example on the use of setTimeout preserving "this".
Like the following:
function doSomething() {
// use 'this' to handle the selected element here
}
$(".someSelector").each(function() {
setTimeout(doSomething.bind(this), 0);
});

Chaining ajax requests with jQuery's deferred

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();
}
}

Categories