This just cried for an elegant solution, and I'm in anything-but-client-work-mood again : ). So here it comes:

Usage:
-----------------------------------------------------------------------------------------------
var Stack = new Callstack();
$.getJSON("jsonA.js", {}, Stack.add('requestA'));
$.getJSON("jsonB.js", {}, Stack.add('requestB'));

Stack.onComplete = function(stack) {
   console.log('All request are completed, see:', stack);
   console.log('Response from requestA:', stack.requestA.arguments[0]);
}
-----------------------------------------------------------------------------------------------

Code you need to use the above:
-----------------------------------------------------------------------------------------------
var Callstack = function() {}
$.extend(Callstack.prototype, {
   stack: {_length: 0}
   , add: function(name, callback) {
       if ($.isFunction(name)) {
           callback = name;
       }
       if (arguments[1] && typeof arguments[1] == 'string') {
           name == arguments[1];
       }
       if (name == '_length') {
           throw 'Callstack::add - Forbidden stack item name "_length"!';
       }
       if (!name) {
           name = this.stack._length;
       }
var item = {
           arguments: []
           , callback: callback
           , completed: false
       };
this.stack[name] = item;
       this.stack._length++;
var self = this;
       return function() {
           self.handle(item, arguments);
       }
   }
   , update: function() {
       var completed = true;
       $.each(this.stack, function(p) {
           if (p != '_length') {
               completed = completed && this.completed;
           }
       });
if (completed == true) {
           delete this.stack['_length'];
           this.onComplete(this.stack);
       }
   }
   , handle: function(item, args) {
       var r;
       if ($.isFunction(item.callback)) {
           r = callback.apply(item.callback, args);
           if (r === false) {
               return r;
           }
       }
item.arguments = args;
       item.completed = true;
       this.update();
       return r;
   }
   , onComplete: function() {
       alert('Stack done executing!');
   }
   , reset: function() {
       this.stack = {};
   }
});
-----------------------------------------------------------------------------------------------

I also threw in some good stuff, for example you do not have to explicitly name you stack items, it will use auto-incrementing values automatically if you don't. Also: You can pass a callback to the Stack.add() function that acts as a gatekeeper for determining if the stack item has executed successfully. If that function returns falls then the stack item will not be thought of as completed and the item can be triggered again using Callstack.handle().

One could also base a little plugin on this that will only trigger a function if all elements in the current selection have triggered a certain event:

Sample usage:
-----------------------------------------------------------------------------------------------
$(function() {
   $('a').stack('click', function(stack) {
console.log('All links in this document where clicked once! See:', stack);
   }, function() {
       // Cancel link default event
       return false;
   });
});
-----------------------------------------------------------------------------------------------

Plugin code:
-----------------------------------------------------------------------------------------------
$.fn.stack = function(event, onComplete, fn) {
   var Stack = new Callstack();
   this.each(function() {
       var stackFn = Stack.add();
       $(this)[event](function() {
           var r;
           if ($.isFunction(fn)) {
               r = fn.apply(this, arguments);
           }
stackFn.apply(this, arguments);
           return r;
       });
   });
   Stack.onComplete = onComplete;
}
-----------------------------------------------------------------------------------------------

Hmm, I guess I should have and eventually will make a blog post about this. Meanwhile I hope you or somebody else finds it useful : ).
-- Felix
--------------------------
My Blog: http://www.thinkingphp.org
My Business: http://www.fg-webdesign.de


Michael Geary wrote:
Good ideas, Thiago. The second one - nested AJAX calls - will definitely work, and it is probably the simplest way to do this:
   $.getJSON("url1", {}, function( json1 ) {
      $.getJSON("url2", {}, function( json2 ) {
         // do something with json1 and json2
      });
   });
That does serialize the two AJAX/JSON calls instead of firing them both off at once, though. (The second request is made when the first one finishes.) If you want to fire the two AJAX calls together (especially a good idea if they are coming from different servers), then you'd want something like your first idea. There is one bug in that code - it will lose the first JSON response, whichever one comes back first. So you would need to add a bit more code to keep track of the two separate JSON objects. There's a similar problem in the Google Mapplet API, where every Maps API information request function such as map.getCenter() runs asynchronously with a callback, much like an AJAX call. You might write mapplet code like this:
map.getSizeAsync( function( size ) {
    map.getBoundsAsync( function( bounds ) {
        map.getCenterAsync( function( center ) {
            // do something with size, bounds, and center
        });
    });
});
I wrote a utility function that reduces the code to:
GAsync( map, 'getSize', 'getBounds', 'getCenter',
    function( size, bounds, center ) {
        // do something with size, bounds, and center
    });
and it runs the three requests in parallel.
As you can see, that's essentially the same problem. It should be easy to adapt this GAsync() code into a jQuery plugin to manage multiple AJAX/JSON requests. I may look at it if I get a little time. The code might look something like this:
   $.getMultiJSON( "url1", {}, "url2", {},
      function( json1, json2 ) {
         // do something with json1 and json2
      });
For a simple case like this it's probably overkill though - with just two JSON responses to keep track of, there are several ways you could easily do it. Or just use the nested callbacks if it doesn't matter that the calls are serialized. -Mike

    ------------------------------------------------------------------------
    *From:* Thiago Ferreira
    just a thought...code will be executed after the second call..

    var oneCall = false;

    function fn(json){
        if(!oneCall){
           oneCall = true;
           return;
         }
         //code here
    }

    $.getJSON("url1", {}, fn);
    $.getJSON("url2", {}, fn);


    or you could put the second ajax call inside the function of the
    first one or the other way around...

    On 8/16/07, *Jones* <[EMAIL PROTECTED]
    <mailto:[EMAIL PROTECTED]>> wrote:


        Hello,

        the following code provides one callback per ajax request:

        $.getJSON("url1", {}, function(json) {
                do something...
        });
        $.getJSON("url2", {}, function(json) {
                do something...
        });

        The problem is, that I need to call a function when both ajax
        requests
        are finished.

        Has anybody an idea?


        Another question: Does anybody know a german jQuery group or
        something
        similar?

        Jones


Reply via email to