Promises were made to solve problems like this; they work really well in functional languages (I've personally used them extensively in Javascript, where curiously jQuery actually has the worst implementation of them - see this comparison). The weird thing about using promises is accepting that things are easier when you make everything use them. I will frame my answer in JavaScript terms using the promise library Q, since that's what I know best - apologies if that's not your cup o' tea, and double apologies for answering a language agnostic question with a specific language and library to boot.
A promise is a wrapper for a callback function. In many libraries, this is called a .then()
function. In simplest terms, a promise waits for a return, and then calls the function passed to then()
. The simplicity was confusing for me in the beginning, so lets use your example. Let me upgrade x
and y
to functions. If they are functions that return promises (as many asynch heavy libraries do, like a NodeJS database accessor), this becomes trivial.
Q.all([x(), y()]).then(function(results) {
// Do some stuff with results, which is an enumerable collection containing
// the in-order results of the promises passed in.
});
The trouble is, what if x()
and y()
don't return promises? As I mentioned, your life gets easier if you make them return promises. Fortunately, any half-decent promise library provides tools for this as well. In fact, there are several ways to do this in Q.
// Upgrade x into a promise returning function
function promise_x(/*x params...*/) {
return Q.fcall(x(/*x params...*/));
}
// Upgrade x into a promise with a deferred
// Useful if you have some standard error handling specific to x-like functions
function defer_x(/*x params...*/) {
var deferred = Q.defer();
// If x is asynch, assume it takes a callback function with params of (error, result)
x(/*x params...*/), function (error, result) {
if (error) {
deferred.reject(new Error(error));
} else {
// If you have some processing to do with the result of x (and not y), you can do
// it here and return the modified result instead
deferred.resolve(result);
}
});
return deferred.promise;
}
If you repeat that code for the y()
function, you get back to where you can use Q.all()
to wait for both x()
and y()
to complete.
Q also provides an API for creating promises, which is like a wrapper function for Q.defer()
.
However, the best feature a promise library provides is aggregated error handling. Expanding on our first Q.all
example, what happens if x or y throw an error? As that code is written now, it gets lost. Consider the following:
MyOverallFunction() {
Q.all([x(), y()]).then(function(results) {
// Do some stuff with results, which is an enumerable collection containing
// the in-order results of the promises passed in.
var combinedResult = synchronousFunction(result[0], result[1]);
anotherAsyncPromiseFn(combinedResult)
.then(function(result)) {
return result;
});
}).then(function(result) {
// Here, result is the return value of anotherAsyncPromiseFn
}).error(function(err) {
// This is the cool part. Any error from x, y, synchronousFunction, or
// anotherAsyncPromiseFn will propagate to here. If you don't want to
// handle it, just re-throw it (after logging it, if you prefer).
});
}
You'll also notice in the above example there are several places where combining sync and async methods is demonstrated.
Ultimately, the tools that are at your disposal depend upon your language and environment. Promises have been implemented in more languages than just JS, and another closely related idiom from async/functional programming is Futures. Admittedly, if your language of choice does not have a Promise library, this answer will be pretty useless to you, but hopefully you at least find it interesting.
Update: Now that you mention Java, it looks like there is a decent promise library in JDefered. I've not used it, but it looks like my examples could be applied with it in a fairly straightforward manner.
f(x,y)
needs to be executed. 3.f(x,y)
runs, completes and sends its answer over the wire. 4. What needs to be deleted? Jus the memory used byf(x,y)
? Shouldn't the garbage collector do everything you need? – Patrick M Jun 24 '14 at 17:16