Sono state fatte promesse per risolvere problemi come questo; funzionano davvero bene in linguaggi funzionali (li ho usati ampiamente in Javascript, dove curiosamente jQuery in realtà ha la peggiore implementazione di essi - vedi questo confronto ). La cosa strana dell'uso delle promesse è accettare che le cose sono più facili quando fai in modo che tutto le usi . Inquadrerò la mia risposta in termini JavaScript utilizzando la promessa libreria Q , poiché è quello che so meglio - scuse se questa non è la tua tazza o "tè e due scuse per rispondere a una domanda agnostica con una lingua e una libreria specifiche da avviare.
Una promessa è un wrapper per una funzione di callback. In molte librerie, questa è chiamata una funzione .then()
. In termini più semplici, una promessa attende un ritorno, quindi chiama la funzione passata a then()
. La semplicità mi ha confuso all'inizio, quindi lascia che usi il tuo esempio. Consentitemi di aggiornare x
e y
alle funzioni. Se sono funzioni che restituiscono promesse (come molte librerie pesanti di asynch, come un accessor del database NodeJS), questo diventa banale.
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.
});
Il problema è che cosa succede se x()
e y()
non restituiscono le promesse? Come ho detto, la tua vita diventa più facile se tu fai loro restituisce promesse. Fortunatamente, qualsiasi libreria promessa semipreziosa fornisce anche strumenti per questo. In realtà, ci sono diversi modi per farlo 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;
}
Se ripeti il codice per la funzione y()
, ritorni a dove puoi utilizzare Q.all()
per attendere sia x()
che y()
da completare.
Q fornisce anche un'API per creare promesse, che è come una funzione wrapper per Q.defer()
.
Tuttavia, la migliore funzionalità offerta da una libreria di promessa è la gestione aggregata degli errori. Espandendo il nostro primo esempio di Q.all
, cosa succede se x o y lanciano un errore? Mentre quel codice è scritto ora, si perde. Considera quanto segue:
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).
});
}
Noterai anche nell'esempio precedente che ci sono diversi punti in cui viene dimostrata la combinazione dei metodi di sincronizzazione e asincrono.
In definitiva, gli strumenti a tua disposizione dipendono dalla tua lingua e dal tuo ambiente. Le promesse sono state implementate in più lingue rispetto al solo JS, e un altro idioma strettamente correlato dalla programmazione asincrona / funzionale è Futures . A dire il vero, se la tua lingua di scelta non ha una libreria Promessa, questa risposta sarà abbastanza inutile per te, ma spero che almeno la trovi interessante.
Aggiornamento: ora che menzioni Java, sembra che ci sia una libreria di buone intenzioni in JDefered . Non l'ho usato, ma sembra che i miei esempi possano essere applicati in modo abbastanza semplice.