K
kj
Pardon the cryptic subject line. This "continuation chaining"
business is my own coinage; I don't know what the correct term is
for what I'm about to describe.
I'm trying to figure out how to program around non-blocking
XMLHttpRequests. For example, suppose I have some function foo
that looks like this:
function foo ( some params ) {
first( x, y, z );
second( 1, 2, 3 );
return;
}
No problem so far. But now suppose that first(), which is called by
foo, in turn sends out some non-blocking XMLHttpRequest:
function first( x, y, z ) {
var request = new XMLHttpRequest();
request.onreadystatechange = function () { /* blah blah */ };
// blah blah
request.open( "GET", some_url, true ); // non-blocking
request.send();
return;
}
In this case, the call to second() in foo will most likely be
executed before the the response to the non-blocking request sent
by first() arrives, and this may not be desirable. In other words,
even though first() returns, it is, in some loose, informal sense,
"not finished". (At least this is the way it is to this plodding,
old-fashioned programmer!)
One kluge to preserve the intuitive temporal order of first() and
second() would be to redefine first() so that it takes an additional
optional argument, which essentially encapsulates second(), and is
later used in the definition of the onreadystatechange callback:
function first ( x, y, z, after ) {
// ...
request.onreadystatechange = function () {
// blah blah
if ( after != null ) after();
return;
};
// ... etc., etc.
And now foo can be re-written:
function foo ( some params ) {
first( x, y, z, null, function () { second( 1, 2, 3 ); } );
return;
}
or possibly,
function foo ( some params ) {
first( x, y, z, function () { second( 1, 2, 3 ); } );
}
That'd work, I guess: first() is the last call in foo, and second() will
get called after the response to the request is received, not
before.
But now we have the same problem with foo as we had with first():
any function that calls foo must ensure that this call is the last
code executed in the function. To circumvent this restriction we
have to redefine foo so that *it* also takes an optional after
argument:
function foo ( some params, after ) {
first(
x, y, z,
function () {
second( 1, 2, 3 );
if ( after != null ) after();
}
);
return;
}
Whew! You see where this is going! Every function that called
first(), and every function that called those, and every one that
called *those*, etc. would need to have this extra optional function
parameter, which gets folded into a growing, hairy BALL OF CALLBACKS,
making the code very difficult to follow.
Is there A Better Way? What is considered "best practice" for
programming around *non-blocking* requests?? (Of course, one could
just use blocking requests, but this introduces its own set of
problems, and is not a solution I am interested in.)
TIA!
kj
business is my own coinage; I don't know what the correct term is
for what I'm about to describe.
I'm trying to figure out how to program around non-blocking
XMLHttpRequests. For example, suppose I have some function foo
that looks like this:
function foo ( some params ) {
first( x, y, z );
second( 1, 2, 3 );
return;
}
No problem so far. But now suppose that first(), which is called by
foo, in turn sends out some non-blocking XMLHttpRequest:
function first( x, y, z ) {
var request = new XMLHttpRequest();
request.onreadystatechange = function () { /* blah blah */ };
// blah blah
request.open( "GET", some_url, true ); // non-blocking
request.send();
return;
}
In this case, the call to second() in foo will most likely be
executed before the the response to the non-blocking request sent
by first() arrives, and this may not be desirable. In other words,
even though first() returns, it is, in some loose, informal sense,
"not finished". (At least this is the way it is to this plodding,
old-fashioned programmer!)
One kluge to preserve the intuitive temporal order of first() and
second() would be to redefine first() so that it takes an additional
optional argument, which essentially encapsulates second(), and is
later used in the definition of the onreadystatechange callback:
function first ( x, y, z, after ) {
// ...
request.onreadystatechange = function () {
// blah blah
if ( after != null ) after();
return;
};
// ... etc., etc.
And now foo can be re-written:
function foo ( some params ) {
first( x, y, z, null, function () { second( 1, 2, 3 ); } );
return;
}
or possibly,
function foo ( some params ) {
first( x, y, z, function () { second( 1, 2, 3 ); } );
}
That'd work, I guess: first() is the last call in foo, and second() will
get called after the response to the request is received, not
before.
But now we have the same problem with foo as we had with first():
any function that calls foo must ensure that this call is the last
code executed in the function. To circumvent this restriction we
have to redefine foo so that *it* also takes an optional after
argument:
function foo ( some params, after ) {
first(
x, y, z,
function () {
second( 1, 2, 3 );
if ( after != null ) after();
}
);
return;
}
Whew! You see where this is going! Every function that called
first(), and every function that called those, and every one that
called *those*, etc. would need to have this extra optional function
parameter, which gets folded into a growing, hairy BALL OF CALLBACKS,
making the code very difficult to follow.
Is there A Better Way? What is considered "best practice" for
programming around *non-blocking* requests?? (Of course, one could
just use blocking requests, but this introduces its own set of
problems, and is not a solution I am interested in.)
TIA!
kj