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