Ergec Ergec - 29 days ago 8
jQuery Question

jQuery deferred works with jQuery v1.x and v2.x but not with v3.x

This fiddle is about jquery deferred objects. Execution order is

1,4,2,3
. It works as expected with jquery
v1.x
and
v2.x
but not
v3.x
. What am I missing?

Proper order is
1,4,2,3,all done
but in
v3
it's
1,2,4,all done,3
which doesn't make sense
when/all done
triggers before
3


Simply just change jQuery version and you'll see that with
v3.x
it doesn't work in the
1,4,2,3
order.


fiddle

https://jsfiddle.net/ergec/mrd2dt3a/

and these are the codes.

css

div {
width: 0px;
height: 20px;
}

.div1 {
background-color: red;
}

.div2 {
background-color: yellow;
}

.div3 {
background-color: lightblue;
}

.div4 {
background-color: gray;
}


javascript

var defer = $.Deferred();
var div1 = defer.then(function(value) {
return $(".div1").animate({
width: "100%"
}, 1000);
});
var div2 = div1.then(function(value) {
$("#status").append("<p>div1 done</p>");
return $(".div2").animate({
width: "100%"
}, 3000);
});
var div3 = div2.then(function(value) {
$("#status").append("<p>div2 done</p>");
return $(".div3").animate({
width: "100%"
}, 2000, function () {$("#status").append("<p>div3 done</p>");});
});
var div4 = function() {
return $.Deferred(function(dfd) {
$(".div4").animate({
width: "100%"
}, 1500, dfd.resolve);
}).promise().done(function () {$("#status").append("<p>div4 done</p>");});
}
$.when(div1, div2, div3, div4()).then(function() {
$("#status").append("<p>all done</p>");
});
defer.resolve();


html

<div class="div1">div1</div>
<div class="div2">div2</div>
<div class="div3">div3</div>
<div class="div4">div4</div>
<span id="status"></span>

Answer

jQuery 3.0 "fixed" the jquery deferred system so that it would more closely follow the Promises/A+ spec. https://jquery.com/upgrade-guide/3.0/#deferred

What this means is now your .then callbacks have to return a thenable, a value, or a rejected promise. jQuery objects aren't thenable, which is why your code started working differently when you upgraded to 3.1.

If you modify your .then() callbacks for 1 2 and 3 to properly return a thenable by adding .promise() to the end, it will go back to working as expected (this works in older versions too.)

/*
JQuery Deferred Objects
*/
var defer = $.Deferred();
var div1 = defer.then(function(value) {
    return $(".div1").animate({
        width: "100%"
    }, 1000).promise();
});
var div2 = div1.then(function(value) {
    $("#status").append("<p>div1 done</p>");
    return $(".div2").animate({
        width: "100%"
    }, 3000).promise();
});
var div3 = div2.then(function(value) {
    $("#status").append("<p>div2 done</p>");
    return $(".div3").animate({
        width: "100%"
    }, 2000, function () {$("#status").append("<p>div3 done</p>");}).promise();
});
var div4 = function() {
    return $.Deferred(function(dfd) {
        $(".div4").animate({
		width: "100%"
		}, 1500, dfd.resolve);
    }).promise().done(function () {$("#status").append("<p>div4 done</p>");});
}
$.when(div1, div2, div3, div4()).then(function() {
    $("#status").append("<p>all done</p>");
});
defer.resolve();
div {
    width: 0px;
    height: 20px;
}

.div1 {
    background-color: red;
}

.div2 {
    background-color: yellow;
}

.div3 {
    background-color: lightblue;
}

.div4 {
    background-color: gray;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="div1">div1</div>
<div class="div2">div2</div>
<div class="div3">div3</div>
<div class="div4">div4</div>
<span id="status"></span>