0

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?

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
TheCarver
  • 19,391
  • 25
  • 99
  • 149
  • Don't you think you should resolve it inside inner `setTimeout`. and return promise from `start`. Then you can use it as `cover.start().then(function () {})` as well – Ehtesham Mar 05 '15 at 13:31
  • I think you need to move .resolve to the `el.filter.animate` callback and `.promise` to the end of the function `start` – lshettyl Mar 05 '15 at 13:32
  • You should try basic debugging before asking questions. You are not returning promise from start as answered but you can easily see that with basic debugging. – Bizniztime Mar 05 '15 at 13:36
  • Just do your homework kid. It has nothing to do with Deferred you are getting 'undefined' value from start() there should be something fishy in your console when you run that. – Bizniztime Mar 05 '15 at 13:45

2 Answers2

4

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
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
1

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.
Roamer-1888
  • 19,138
  • 5
  • 33
  • 44