TheCarver
TheCarver

Reputation: 19713

jQuery deferred - when multiple timeout tasks have completed

Having a few teething problems with $.deferred, $.when and $.done.

I'm calling a method which has a couple of tasks inside on a timer. I'm looking at getting a callback when everything inside this method has completed, including the stuff in timers, so started looking at $.when() and $.done() to achieve this.

The problem I am getting is the function is firing before the tasks have completed, immediately as the method is called. So, I started playing with $.deferred and resolve(), but haven't managed to get anything working. Without the timers, I can do it.

This is where I call the method:

$.when(cover.start()).done(function() {
    console.log("Cover has started.");
});

This is the entire method:

return {

    other: function() {},

    start: function() {
        var dfd = $.Deferred();
        el.filter.animate({
            "opacity": "0.6", "filter": "alpha(opacity=60)"
        }, 2000, "easeInOutCirc", function() {
            el.share.removeClass('fa-spin');

            setTimeout(function() {
                el.share.removeClass('fa-cog').addClass('fa-bars');
            },1000);

            setTimeout(function() {
                el.scroll.animate({
                    "opacity": "1",
                    "bottom": "40px"
                }, 1200, "easeOutBounce", function() {
                    var pulseOptions = { opacity: "0" };
                    setTimeout(function() {
                        el.scroll.pulse(pulseOptions, {
                            duration : 400,
                            pulses: 3,
                            interval: 500,
                            returnDelay: 800
                        });
                    }, 2000);
                    dfd.resolve();
                });
            }, 2000);

            return dfd.promise();

        });
    }

} // end return

As you can see, after my original attempt failed, I added dfd.resolve() to where I want the callback and tried to return the promise. However, the function still fires too early. Where am I going wrong?

Upvotes: 0

Views: 181

Answers (2)

Roamer-1888
Roamer-1888

Reputation: 19288

Not fishing to steal APJ's rep' but out of interest, you could avoid callback hell by exploiting .delay() and .promise(), both of which relate to the default "fx" animation queue.

Something along the following lines would fix the problem, and would be more readable :

//animation maps
var maps = [];
maps[0] = { 'opacity':0.6, 'filter':'alpha(opacity=60)' };
maps[1] = { 'opacity':1, 'bottom':'40px' };
maps[2] = { 'opacity':0 };
maps[3] = { 'duration':400, 'pulses':3, 'interval':500, 'returnDelay':800 };

//animation functions
var f = [];
f[0] = function () {
    return el.filter.animate(maps[0], 2000, "easeInOutCirc").promise();
};
f[1] = function () {
    return el.share.removeClass('fa-spin').delay(1000).promise();
};
f[2] = function () {
    return el.share.removeClass('fa-cog').addClass('fa-bars').delay(1000).promise();
};
f[3] = function () {
    el.scroll.animate(maps[1], 1200, "easeOutBounce").promise();
}
f[4] = function () {
    return el.scroll.delay(2000).promise();//delay() could be called on any element. `el.scroll` is arbitrary.
};
f[5] = function () {
    el.scroll.pulse(maps[2], maps[3]);
};

return {
    other: function () {},
    start: function () {
        //animation sequence
        var p = f[0]().then(f[1]).then(f[2]).then(f[3]);
        p.then(f[4]).then(f[5]);
        return p;//<<<< and here's the all important return
    }
}

Not sure this is 100% correct - might need some work.

It's worth noting that there are performance pros and cons with this approach :

  • Pros: Reusable animation maps; Reusable functions;
  • Cons: More liberal use of promises will cause a larger memory spike.

Upvotes: 1

Arun P Johny
Arun P Johny

Reputation: 388316

The problem is, you need to return promise from the start method

return {

    other: function () {},

    start: function () {
        var dfd = $.Deferred();
        el.filter.animate({
            "opacity": "0.6",
                "filter": "alpha(opacity=60)"
        }, 2000, "easeInOutCirc", function () {
            el.share.removeClass('fa-spin');

            setTimeout(function () {
                el.share.removeClass('fa-cog').addClass('fa-bars');
            }, 1000);

            setTimeout(function () {
                el.scroll.animate({
                    "opacity": "1",
                        "bottom": "40px"
                }, 1200, "easeOutBounce", function () {
                    var pulseOptions = {
                        opacity: "0"
                    };
                    setTimeout(function () {
                        el.scroll.pulse(pulseOptions, {
                            duration: 400,
                            pulses: 3,
                            interval: 500,
                            returnDelay: 800
                        });
                    }, 2000);
                    dfd.resolve();
                });
            }, 2000);


        });
        //need to return from start
        return dfd.promise();
    }

} // end return

Upvotes: 4

Related Questions