3

Possible Duplicate:
Continue Execution Only After .each() Completes

This question is actually a continuation from this discussion. How can we wait each() to finish its execution given that there is $.get() inside its callback function?

Working example can be found here.

/* JavaScript / jQuery. */
<script>        
function prepareLayer($n) {
    $.get('./a.html', function(data) {
        /* a.html contains: <a href="javascript:void(0);">Click me!</a> */
        $n.html(data);
    });
}

function postPreparation() {
    $('.element a').click(function() {
        alert('Ouch... you just clicked me!');
    });
}

$(function() {
    $('.element').each(function() {
        prepareLayer($(this));
    });

    postPreparation();
});
</script>

<!-- HTML -->
<div class="element"></div>
<div class="element"></div>
Community
  • 1
  • 1
moey
  • 10,587
  • 25
  • 68
  • 112
  • 4
    Please update your previous question instead of reposting with just minor updates. Also Alnitaks answer is allready including solutions for async requests. – Yoshi Jan 28 '12 at 10:15
  • Wait @Yoshi, the accepted answer in that question doesn't provide a solution on how to solve this. It only confirmed the fact / problem described here. – moey Jan 28 '12 at 10:20
  • Well sure it does, if you'll have a look at the doc for [.when](http://api.jquery.com/jQuery.when/) – Yoshi Jan 28 '12 at 10:22
  • 2
    Glad this hasn't been closed yet. @nikoshr provided the working answer. The 'possible duplicate' question mentioned above provides a good background and discussion. – moey Jan 28 '12 at 11:46

1 Answers1

12

@Alnitak gave you most of the solution in this question : Continue Execution Only After .each() Completes

var def = [];
$('.element').each(function() {
    // have prepareLayer return a _promise_ to return
    def.push(prepareLayer());
}

// use "when" to call "postPreparation" once every
// promise has been resolved
$.when.apply($, def).done(postPreparation);

The missing piece would look like

function prepareLayer($n) {
    var dfd=$.Deferred();
    $.get('./a.html', function(data) {
        /* a.html contains: <a href="javascript:void(0);">Click me!</a> */
        $n.html(data);
        dfd.resolve();
    });
    return dfd.promise();
}

Or with jQuery>=1.8, courtesy of @jfriend00

function prepareLayer($n) {
    return $.get('./a.html').then(function(data) {
        $n.html(data);
    });
}
Community
  • 1
  • 1
nikoshr
  • 32,926
  • 33
  • 91
  • 105
  • Now, this is the real solution! Thank you very much (+1), @nikoshr. – moey Jan 28 '12 at 11:42
  • `$.when(def).done(postPreparation)` (i.e. without _apply()_) will immediately execute `postPreparation`. Do you know why this is the case? I mean, why passing `$` as the context in _apply()_ makes _when()_ to wait? – moey Dec 16 '12 at 16:23
  • 2
    @Siku-Siku.Com The context is irrelevant, the method signature is. $.when accepts one or more Deferred objects, or plain JavaScript objects as its arguments and calling apply on it transforms the def array to its arguments. `$.when(def)` falls into the case where the argument is not a Deferred and thus is immediately resolved. See http://api.jquery.com/jQuery.when/ – nikoshr Dec 17 '12 at 11:38
  • Ah... it's clear now; thanks (+1) for your explanation. – moey Dec 17 '12 at 16:03
  • Fabulous answer to a very common problem very badly documented in jQuery doc. Thank's for your explanation (+1010101 if I could). – jmcollin92 Jan 31 '13 at 22:40
  • @jfriend00 In that case, you couldn't guarantee the promises resolve *after* the HTML fragments have been inserted – nikoshr Aug 23 '14 at 09:06
  • @nikoshr - no that's not true. You just do this: `return $.get('./a.html').then(function(data) {$n.html(data)});` and the returned promise does not resolve until after the data is in the HTML. – jfriend00 Aug 23 '14 at 14:53
  • @jfriend that's what I'm saying, you have to create a new deferred, by $.get.then or by explicitely creating one. Returning $.get as your previous comment advised would not work. – nikoshr Aug 23 '14 at 15:30
  • @nikoshr - It will work. `.then()` actually creates a second promise for you that doesn't resolve until after it's handler returns. It is that second promise that is returned from the function and it WILL work. This design pattern is used all the time to do exactly what the OP wants to do. You do not have to create your own defer to solve this problem. – jfriend00 Aug 23 '14 at 15:33
  • Please reread my comment, I agree that .then will work, but your now deleted comment advised to use return $.get and that was what I responded to. – nikoshr Aug 23 '14 at 15:37
  • @nikoshr - I deleted my first comment because I realized the wording in it wasn't ideal. I am suggesting that you use the promise returned from `$.get()` rather than creating your own. I haven't downvoted your answer. I was trying to help you improve it. You are using the promise anti-pattern labeled "The Forgotten Promise" in [this article](http://taoofcode.net/promise-anti-patterns/) about undesirable ways to use promises. Using `return $.get('./a.html').then(function(data) {$n.html(data)});` will solve the problem more elegantly. I hope you can improve your answer. – jfriend00 Aug 23 '14 at 15:42
  • @nikoshr - so now that I think we're on the same page here, are you going to improve your answer? – jfriend00 Aug 23 '14 at 15:46