5

I'm trying to get my head around the whole deferred concept and I'm trying to use it to synchronize a fadeIn/fadeOut animation together with an Ajax call.

Basically I'm switching content on page doing:

  1. Fetch content with ajax
  2. On response FadeOut
  3. Replace content
  4. FadeIn

However, If i understand deferreds right I might be able to do something like this:

  1. fadeOut, and at the same time initialize Fetch content with ajax
  2. When both the fadeOut and the Fetch content are complete: Change content
  3. FadeIn

Some code of the original solution:

$.get(url, function(page) {
    $('#content').fadeTo(100, 0, 'linear', function() {
        $(this).html(page.text).fadeTo(400, 1, 'linear');
    });
}

I am trying to do something like this:

var deferred1 = $.get(url);
var deferred2 = $('#content').fadeTo(100, 0, 'linear').promise();

$.when(deferred1, deferred2).done(function() {
    $('#content').html(page.text).fadeTo(400, 1, 'linear');
});

I just can't really get clear on how to use it. And should i use done or then? Should I use pipe in a clever way? Do I need promise?

What would be the more 'standardized' way to implement this?

Yonder
  • 719
  • 2
  • 13
  • 26

1 Answers1

6

When you use $.when (I know, funny sentence ;)), then the values with which the promises are resolved are passed as arguments to the callback functions. I.e. in your done callback, the first argument refers to the resolved value of deferred1 and the second argument to deferred2 (whatever that is).

Now, when an Ajax success callback is called, it gets passed three arguments:

  • the response
  • the status text
  • the jqXHR object

You are only interested in the first.

So with this, your setup should be:

var promise1 = $.get(url);
var promise2 = $('#content').fadeTo(100, 0, 'linear').promise();

$.when(promise1, promise2).done(function(ajax_success) {
    $('#content').html(ajax_success[0]).fadeTo(400, 1, 'linear');
});

Also have a look at the $.when documentation.


Do I need promise?

No. It looks like $.when calls .promise internally when you pass a jQuery collection. But in other cases you would have to, so it makes sense to do this here as well, for consistency (thanks Alnitak).

And should i ... ? Should I ...? What would be ...?

There is no standard solution for this kind of problems. Promises are incredibly flexible and there are so many ways to use. I think they are not around long enough in JavaScript for standard patterns to emerge. Find/create something that makes sense to you and be consistent within your application.

The only thing I would recommend to do if you have multiple promises is put them in an array: jQuery $.when() with variable arguments.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • are you sure he doesn't need `.promise()` - AIUI a jQuery object doesn't automatically implement the promise interface to indicate animation completion, you have to call `$(...).promise()` to get the desired promise. – Alnitak Apr 12 '13 at 11:18
  • @Alnitak: Afaik it does. Unless it was changed recently. *edit:* Still works: http://jsfiddle.net/y5Fk7/. – Felix Kling Apr 12 '13 at 11:19
  • not that I know of, it's been that way since animation started supporting deferred in 1.6. See `http://api.jquery.com/promise/` vs the `deferred.promise()` function. Calling `.promise` on a deferred just ensures you only get a (stripped back) object that only implements the promise interface, but calling `.promise` on a jQuery object is necessary to actually create the dynamic promise that will watch that element's animation queue. – Alnitak Apr 12 '13 at 11:21
  • But you see in the third example how `.promise` is not called? Maybe `$.when` calls `.promise` internally when a jQuery collection is passed. – Felix Kling Apr 12 '13 at 11:22
  • Yes, that's my theory too - I'm looking into that now. – Alnitak Apr 12 '13 at 11:24
  • Updated my answer and will update again if you find something more specific. – Felix Kling Apr 12 '13 at 11:25
  • It looks like `$.when()` might call `obj.promise()` on any object passed that has such a method, and jQuery objects do have it. My general advice would be to explicitly ask for the `.promise()` in case it's being used by a method other than `$.when()`. – Alnitak Apr 12 '13 at 11:27
  • 1
    It seems to work with multiple objects as well (http://jsfiddle.net/y5Fk7/1/). Anyways, I updated my answer. – Felix Kling Apr 12 '13 at 11:32
  • +1 Thanks a lot guys, it seems promise() is not needed, and I can change .done to .then and that fiddle still seems to work. .then seems like a more flexible version of .done in the way that you can specify fail callbacks and progress callbacks – Yonder Apr 12 '13 at 11:38
  • Just a quick one, Why does Promise1 not call .promise()? – Liam Apr 12 '13 at 13:19
  • 1
    @Liam: All jQuery Ajax methods return a `jqXHR` object which already implements the promise interface. – Felix Kling Apr 12 '13 at 13:25
  • 1
    All jQuery Ajax methods **except `.load()`** return a jqXHR object. [`.load()`](http://api.jquery.com/load/) returns jQuery for standard chaining. – Beetroot-Beetroot Apr 13 '13 at 01:35