jQuery deferred and promise not work corectly - javascript

I have this code:
var d = $.Deferred();
var promise = d.promise();
//
promise = promise.then(function(){
var t = new $.Deferred();
var cont = $('.cont').find('> div');
requirejs(["transit"], function(){
cont.transit({opacity: .4});
//
setTimeout(function(){
cont.remove();
return t.promise();
}, 500);
});
});
//
promise.then(function() {
console.log('test');
});
//
d.resolve();
I wanna to fire up some action after another. But i need to be shure that the first one is finished. So i use promise and deferred methods. Something is not right bc second action starts before defined timeout delay. What is wrong? Can anybody help?

chain does not correctly setup?
The t promise has not been returned to the chain of d.
Modified code
var d = $.Deferred();
var promise = d.promise();
//
promise = promise.then(function(){
var t = new $.Deferred();
console.log('1st promise callback');
//var cont = $('.cont').find('> div');
setTimeout(function(){
//requirejs(["transit"], function(){
//cont.transit({opacity: .4});
//
console.log('timeout 1 func');
setTimeout(function(){
console.log('timeout 2 func');
//cont.remove();
t.resolve(true);
}, 10);
},30);
return t.promise();
});
//
promise.then(function() {
console.log('test');
});
//
d.resolve();
Output
1st promise callback (index):27
timeout 1 func (index):33
timeout 2 func (index):36
test
fiddle here

Why not use the $.when
$.when(function).done(function( x ) {
console.log('test');
});

You are using some Kind of non-Jquery, asynchronous (which is completely unnecessary if you already have jQuery) function- as asynchronous operations run on a separate thread the caller will finish nigh-instantly. If you really want to ensure sequential execution you may simply use a proper jQuery UI method (like animate : http://api.jquery.com/animate/) and add a complete-handler, as documented in the link

Related

Jquery Deferred Not reaching last .then

I have a sequence of functions that must be executed. They all execute sequentially except the last one. d1 executes, d2 executes, d3 executes then the code inside the done function executes before the resolve of d4. Can't figure out why. Any help would be appreciated.
$(document).ready(function() {
var d1 = functiond1();
var d2 = functiond2();
var d3 = functiond3();
var d4 = functiond4();
d1.then(d2).then(d3).then(d4).done(function() {
//Code here does not wait for d4 to end before executing
//HELP!
});
});
function functiond1() {
var dfd = $.Deferred();
//Do stuff here
//Works in sequence
dfd.resolve();
return dfd.promise();
}
function functiond2() {
var dfd = $.Deferred();
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
jQuery.getJSON($.webMethoJSONGet1, params).done(function(data) {
//Do stuff here
//Works in sequence
dfd.resolve();
});
return dfd.promise();
}
function functiond3() {
var dfd = $.Deferred();
//Do stuff here
//Works in sequence
dfd.resolve();
return dfd.promise();
}
function functiond4() {
var dfd = $.Deferred();
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
jQuery.getJSON($.webMethoJSONGet2, params).done(function(data) {
//Do stuff here
//does not work in sequence
dfd.resolve();
});
return dfd.promise();
}
It's hard to tell what you are trying to do with those promises. You first call all 4 functions, and then you try to chain them with a bunch of then callbacks. If you want to sequentially chain them together it should look like this:
functiond1()
.then(functiond2)
.then(functiond3)
.then(functiond4)
.done(function() { /* blah */ });
If you just want a result after all have completed you can use $.when
$.when(functiond1(), functiond2(), functiond3(), functiond4())
.then(function(resultd1, resultd2, resultd3, resultd4) { /* blah */ });
On another note, in your functions you create promises that are resolved inside the done callback of another promise which is unnecessary. The $.getJSON.done() calls return a promise themselves so an additional promise is not needed. Just return the promise returned from done().
Sorry, I haven't messed much with jQuery deferred objects, but they appear similiar enough to standard Promises.
To run the functions in sequence, you need to pass references to the functions within the .then chain, and not the results of calling those functions.
e.g.
var d1 = functiond1; // NB: no ()
...
d1.then(d2).then(d3).then(d4).done(...);
functiond1().then(functiond2).then(functiond3).then(functiond4).done(...)
The ultimate cause of your problem is that invoking d4 straight away will cause its resolved promise to pass-thru to .done immediately irrespective of the state of the earlier part of the .then chain.
You should also not wrap your JSON functions with additional promises, since $.getJSON already returns a promise that will be rejected if the AJAX query fails:
function functiond4() {
...
return $.getJSON(...);
}
I was facing the same issue on a project, this solution with an array works well:
$(document).ready(function() {
var pr = [];
var d1 = functiond1();
var d2 = functiond2();
var d3 = functiond3();
var d4 = functiond4();
function functiond1() {
var dfd = $.Deferred();
pr.push(dfd);
setTimeout(function(){
$('body').append('1 resolved <br>');
dfd.resolve();
}, 2000);
}
function functiond2() {
var dfd = $.Deferred();
pr.push(dfd);
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
setTimeout(function(){
$('body').append('2 resolved <br>');
dfd.resolve();
}, 3000);
}
function functiond3() {
var dfd = $.Deferred();
pr.push(dfd);
setTimeout(function(){
$('body').append('3 resolved <br>');
dfd.resolve();
}, 1000);
}
function functiond4() {
var dfd = $.Deferred();
pr.push(dfd);
params = jQuery.param({
'parm1': 1,
'parm2': 2,
'parm3': 3
});
setTimeout(function(){
$('body').append('4 resolved <br>');
dfd.resolve();
}, 50);
}
$.when.apply($, pr).then(function() {
// do something
$('body').append('proceed with code execution');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

How to use $q.deffer to chain ajax requests?

I need to do this: browser have to make N requests to the server, requests mustn't be async, next requests are starting after previous requests will stop.
I can write some function A with for i < N i++ and calling this function A again recursively to do this, but it is not beautifull at all. Also, this called callback hell. I want some more beautifull solution.
I found deffered objects. Some says, it can help me to escape callback hell. I want something like this. setTimeout there is imitate one async request:
function foo1(some) {
debugger;
setTimeout(function foo1async() {
debugger;
deffered.resolve();
}, 500);
return deffered.promise;
}
function foo2(some) {
debugger;
setTimeout(function foo2async() {
debugger;
deffered.reject();
}, 500);
return deffered.promise;
}
function foo3() {
debugger;
setTimeout(function foo3async() {
debugger;
deffered.resolve();
}, 500);
return deffered.promise;
}
var deffered;
function doChain() {
debugger;
deffered = $q.defer();
var promise = deffered.promise;
promise.then(foo1);
promise.then(foo2);
promise.then(foo3);
promise["finally"](function () {
debugger;
});
deffered.resolve();
}
I expect foo1 to be called, then foo1async will be called and resolve deffered object.
foo2 must be called, then foo2async is called.
3.Now I expect, that foo3 wouldn't start, because deffered is rejected in foo2async. After that I expect foo in finally section called.
Actually, I have this:
foo1, foo2 and foo3 are called. Then foo in finally section called. Then foo1async, foo2async and foo3async funtions are called.
How I can get what I am expecting?
Actually, I will have something like this:
for(var i = 0; i < N; i++) {
(function (iter) {
promise.then(function () {
foo(iter);
});
})(i);
}
You got a few things wrong here.
First, you use a deferred to convert a callback-based async function into a promise-based - but each one needs its own deferred.promise and thus its own deferred. Actually, I prefer to use the $q constructor instead:
function fooN(input){
return $q(function(resolve, reject){
setTimeout(function(){
resolve(input + "; some more data");
}, 500);
});
}
(you could use var deferred = $q.defer() as well)
fooN now returns a promise, so you don't need to use $q.defer() anymore.
In fact, if the async function already was promise-based, like $timeout or $http, then you wouldn't have needed a deferred at all, for ex:
function fooN(input){
return $timeout(function(){
return input + "; some more data";
}, 500);
})
So, let's assume that foo1, foo2 and foo3 are implemented like fooN - all returning promises.
To make the calls sequential, you would need to chain promises - not to attach multiple handlers to the some root promise.
I'll break it down for you:
function doChain(){
var foo1Promise = foo1();
var foo2AfterFoo1Promise = foo1Promise.then(foo2);
var foo3AfterFoo2Promise = foo2AfterFoo1Promise.then(foo3);
var promise = foo3AfterFoo2Promise.then(function(finalData){
return doSomeProcessing(finalData); // if needed
});
promise.catch(function(error){
// "rethrow", if can't handle
return $q.reject({msg: "Some error occurred"});
})
return promise;
}
Or, the same, more concise:
function doChain(p){
return foo1(p)
.then(foo2)
.then(foo3)
.then(function(finalData){
return doSomeProcessing(finalData);
})
.catch(function(error){
return $q.reject({msg: "Some error occurred"});
});
}
A "promised" return value of each function is an input to the next chained function.
You can use $q.all method. For instance:
var promises = [promise1, promise2, ...];
$q.all(promises).then(function () {
// do something
});
What happens now is that all foo* promises depend on the single promise; when it gets resolved all are triggered. In ASCII art the dependencies are:
┎ foo1
promise ╁ foo2
┖ foo3
What you want is:
function doChain() {
foo1()
.then(foo2)
.then(foo3)
;
}
No need for the extra promise. No callback hell either!

Run function after 1sec from loading [duplicate]

I want to have one callback function after actions are done, I'm trying something like this:
$.when(
$('#detail1').load('/getInfo.php'),
$('#detail2').load('/getOther.php')
).then(function(a,b){
alert("done");
});
The problem is that the callback function is firing before the actions are finished.
This is because jQuery.when() expects jQuery.Deferred instances while load() returns an jQuery instance (see http://api.jquery.com/jQuery.when/ and http://api.jquery.com/load/).
You can work around this issue:
// Create two Deferred instances that can be handed to $.when()
var d1 = new $.Deferred();
var d2 = new $.Deferred();
// Set up the chain of events...
$.when(d1, d2).then(function() {
alert('done');
});
// And finally: Make the actual ajax calls:
$('#detail1').load('/getInfo.php', function() { d1.resolve(); });
$('#detail2').load('/getOther.php', function() { d2.resolve(); });
I do a similar code but for images in a more dynamic way. Hope that it help.
var deferreds = [];
// Create a deferred for all images
$('.my-img-class').each(function() {
deferreds.push(new $.Deferred());
});
var i = 0;
// When image is loaded, resolve the next deferred
$('.my-img-class').load(function() {
deferreds[i].resolve();
i++;
});
// When all deferreds are done (all images loaded) do some stuff
$.when.apply(null, deferreds).done(function() {
// Do some stuff
});

How do I dynamically add a deferred to the promise from this jsFiddle?

Regarding this jsFiddle, I am trying to dynamically add a "deferred" which is created when an event triggers, so the done callback is only called when all deferred are resolved, including those added later:
Relevant code:
var promises = [ deferred1, ... ];
var p = when.all(promises).then(function() {
console.log('All done!!');
//! trigger
});
promises.push( deferredFromEvent ); // << ignored
update:
suggestions using Q or jQuery are welcome, I am looking for one that works
Treat your fixed promises as a separate bundle than the dynamic one(s).
Wait on the bundled promises and then pipe their combined outcome through a $.when() on an initially empty array. You can dynamically push new promises into this array any time.
http://jsfiddle.net/atesgoral/YVkVa/1/
HTML:
<p>You have 5 seconds to inject!</p>
<button id="inject">Inject Deferred 3</button>
<button id="resolve">Resolve Deferred 3</button>
<p>Deferred 1: <span id="dfd1">pending<span></p>
<p>Deferred 2: <span id="dfd2">pending<span></p>
<p>Deferred 3: <span id="dfd3">pending<span></p>
<h1 id="result"></h1>
JavaScript:
var dfd1 = $.Deferred(),
dfd2 = $.Deferred(),
fixed = [ dfd1.promise(), dfd2.promise() ],
multiplexed = $.when.apply($, fixed), // Bundle the fixed ones
reservoir = []; // Reservoir for dynamic promises
// The solution to your problem lies here. The rest is scaffolding.
var ultimate = multiplexed.pipe(function () {
return $.when.apply($, reservoir);
});
ultimate.done(function() {
$("#result").text("Done!");
});
window.setTimeout(function () {
dfd1.resolve();
$("#dfd1").text("resolved");
}, 2500);
window.setTimeout(function () {
dfd2.resolve();
$("#dfd2").text("resolved");
}, 5000);
var dfd3 = $.Deferred();
$("#inject").click(function () {
reservoir.push(dfd3.promise());
});
$("#resolve").click(function () {
dfd3.resolve();
$("#dfd3").text("resolved");
});
Create all of your promises that you know you need up-front. Build a .when with them. Save the promise returned from the .when. As you add new events, which require new promises, add new .whens, using the promise from previous .whens, plus whatever new promises you've completed.
Your application will have multiple single points of failure, if you're using the final .when as your "Go ahead with the app.". IE: if any one promise fails at any point, then any .when created after that is also going to fail.
...but if that's your intent, or you've got some solid error-handling, it should do you.
I'm trying to keep this library-agnostic -- usually, I use my own implementation, which is half-way between something jQuery does, and something that Crockford did in a recent talk, but if we assume that:
functions return promise-handlers
"when" returns a promise-handler
promise-handlers have at least a .done and .fail -- or accept two arguments, or whatever
and whatever happens inside of a function will control whether the promise is rejected/resolved or kept/broken (or whatever), then you might end up with a bunch of functionality that looks like:
var doing = doSomething(), // returns promise
making = makeSomething(), // returns promise
loading = loadSomething(), // returns promise
dependencies_lvl_1 = when(doing, making, loading);
Later on, you might add a new module or widget -- maybe it saves some work:
var saving = saveSomething(), //returns promise
dependencies_lvl_2 = when(dependencies_lvl_1, saving);
Maybe after that, you need to switch pages, but you need your data to cache first
var caching = cacheData(), // returns promise
when(dependencies_lvl_2, caching)
.done(goToNextPage)
.fail(handleError);
If you look at it, you know for a fact that as long as when returns a promise which only succeeds if all promises are kept (and when all promises are kept), and none of them broke, then dependencies_lvl_2 there, includes all dependencies from dependencies_lvl_1, plus additional promises.
The level-3 .when, then, has a resolution depending on every single thing which has been added to the chain.
And as long as you keep caching your promises into variables (or some sort of future-access), you can keep chaining these together, into eternity.
"... so the done callback is only called when all deferred are resolved, including those added later" makes no sense, however I think I know what you mean.
If I understand correctly then you want what might be termed a "re-firable when()" - something like this (based on jQuery) :
function PromiseSet(memory, once) {//javascript Constructor
var flags = [];
if(memory) flags.push('memory');
if(once) flags.push('once');
var promises = [],
doneCallbacks = $.Callbacks(flags.join(' ')),
failCallbacks = $.Callbacks(flags.join(' '));
this.add = function(promise, val) {
promises.push(promise);
if(val) { this.fire(val); }
return this;
};
this.done = function(fn) {
doneCallbacks.add(fn);
return this;
};
this.fail = function(fn) {
failCallbacks.add(fn);
return this;
};
this.fire = function(val) {
val = val || null;
$.when.apply($, promises).then(
function() { doneCallbacks.fire(val); },
function() { failCallbacks.fire(val); }
);
return this;
};
return this;
}
untested
All methods return this to make them chainable.
If I've written the constructor correctly, then you can control its detailed behaviour by passing booleans to new PromiseSet(). For your propised usage, I think you need to pass (true, false), but try other settings to see what happens.
Sample sequence :
var myPromiseSet = new PromiseSet(true, false);
myPromiseSet.add(promise1);
myPromiseSet.add(promise2).add(promise3);
myPromiseSet.done(myDoneFunction).fail(myFailFunction);
myPromiseSet.fire("foo");
myPromiseSet.add(promise4).fire();
Take a look to this solution. By this implementation you able to track a lot of dinamic added promises. If you are using jQuery Deferred you could do this way.
jsFiddle
note that I've used _.every method from lodash library, so you'll need to install lodash too
function doAlotOfAsyncThings(callback) {
var promises = [];
var def = $.Deferred();
console.log('Adding first promise');
promises.push(def.promise());
setTimeout(function() {
// Dinamically adding second promise. Important note: last added promise must be added to array BEFORE previous promise has been resolved
var def2 = $.Deferred();
var def3 = $.Deferred();
console.log('Dinamically adding second and third promises');
promises.push(def2.promise());
promises.push(def3.promise());
console.log('Resolving first promise');
def.resolve();
setTimeout(function() {
console.log('Resolving second and third promises');
def2.resolve();
def3.resolve();
}, 1500);
}, 1000);
function checkAllDone() {
console.log('Checking $.when');
$.when.apply(null, promises).then(function() {
// we have to check state of every promise in array
var all_resolved = _.every(promises, function(elem) { return elem.state() == 'resolved'; });
if(all_resolved) {
console.log('All promises are resolved! callback();')
callback();
} else {
console.log('Hm, seems that some promises were dinamically added, waiting for them..');
checkAllDone();
}
});
}
checkAllDone();
}
doAlotOfAsyncThings(function(){
console.log('Done');
});
With Q.js it's even shorter.
In this solution I use _.any method from lodash library
function doAlotOfAsyncThings(callback) {
var promises = [];
var def = Q.defer();
console.log('Adding first promise');
promises.push(def.promise);
setTimeout(function() {
// Dinamically adding second promise. Important note: last added promise must be added to array BEFORE previous promise has been resolved
var def2 = Q.defer();
var def3 = Q.defer();
console.log('Dinamically adding second and third promises');
promises.push(def2.promise);
promises.push(def3.promise);
console.log('Resolving first promise');
def.resolve();
setTimeout(function() {
console.log('Resolving second and third promises');
def2.resolve();
def3.resolve();
}, 1500);
}, 1000);
function checkAllDone() {
console.log('Checking $.when');
Q.all(promises).then(function() {
// Q remove resolved promises from array so we have to check if array contains some non-undefined elements (those elements would be non-resolved dinamically added promises)
if(_.any(promises)) {
console.log('Hm, seems that some promises were dinamically added, waiting for them..');
checkAllDone();
} else {
console.log('All promises are resolved! callback();')
callback();
}
});
}
checkAllDone();
}
doAlotOfAsyncThings(function(){
console.log('Done');
});

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

Categories