Ho adottato uno schema in uno dei miei progetti che mi piace molto e sospetto che sia probabile che sia qualcosa di standard. Mi piacerebbe descriverlo qui e vedere se voi ragazzi potete dirmi come altri programmatori JavaScript risolvono questo tipo di problema.
Alcune regole che ho stabilito per me stesso prima di iniziare il progetto. 1. Valutazione funzionale / pigra laddove possibile. Non fare un sacco di cose all'avvio. 2. Usa le promesse (mi capita di usare Angular's $ q, ma la sintassi è molto simile)
Per semplicità, diciamo che sto volendo aggiornare lo schermo e visualizzare dire in alto il nome dell'utente, quanti Mi Piace hanno, e il loro attuale Conteggio amici. Alcuni di questi possono sembrare artificiosi, ma nudi con me.
Nel mio controller sul lato client farei delle semplici chiamate a un servizio lato client che faccia qualcosa del tipo:
function getCurrentUserName() {
getCurrentUser().then( function(user) {
return user.name;
}
}
function getCurrentUserLikeCount() {
getCurrentUser().then( function(user) {
return user.likeCount;
}
}
function getCurrentUserFriendCount() {
getCurrentUser().then( function(user) {
return user.friendCount;
}
}
Ora potrei avere più elementi dell'interfaccia utente che accedono al controller e chissà in che ordine potrebbero arrivare le richieste. Diamine, è possibile che ci possa essere un widget di terze parti che accede al controller, quindi potrebbero esserci più chiamate a una di queste routine in arrivo rapidamente.
Ora, il primo problema ovvio è che getCurrentUser () è asincrono e fa una specie di chiamata ajax / resto al server. Così: 1) Non è necessario effettuare chiamate inutili al server. Se getCurrentUser () è già stato chiamato, non è necessario chiamarlo di nuovo (dato un po 'di logica come l'utente è ancora loggato, ecc.)
Quindi supponiamo che getCurrentUser () sia abbastanza intelligente da memorizzare nella cache i risultati. Nelle chiamate successive, potrebbe restituire il valore memorizzato nella cache (sempre in modo asincrono usando when ()).
Il secondo problema è, cosa succede quando getCurrentUser () viene chiamato mentre un'altra chiamata a getCurrentUser () è già in elaborazione. La cache non è stata ancora aggiornata, quindi la seconda chiamata non vede la cache, quindi fa un'altra chiamata HTTP al server. Tu non vuoi quello. Quindi devi sapere che c'è una richiesta in sospeso al server e limitare la chiamata.
Quindi lo schema che ho adottato è che una funzione asincrona che effettua una chiamata al server segue la seguente idea di alto livello: 1. Se è la prima chiamata, invia la chiamata al server e restituisci immediatamente una promessa al cliente. Inoltre, aggrappati a questa promessa. Quando il server risponde, risolvi la promessa ma mantieni anche il valore originale restituito dal server (cioè la cache)
-
Se non è la prima chiamata, determina se la prima chiamata è stata completata. Se il primo chiamato è già stato completato, restituire il valore restituito dal server (ad esempio la cache). Certo, lo faresti usando when () dal momento che il client si aspetta una promessa.
-
Se non è la prima chiamata, ma la prima chiamata non è ancora stata completata, quindi restituire al client la promessa memorizzata nella cache che è stata restituita dalla prima chiamata. Quando poi la promessa viene risolta, tutti i client vengono avvisati.
In effetti, ciò che funziona anche con l'angolare $ q (e probabilmente altri), è quello di riutilizzare la promessa originale anche dopo che è stata restituita in passato. Quindi puoi evitare di memorizzare i dati nella cache poiché la promessa contiene i dati restituiti dal server.
Io chiamo tutto ciò che strozza, per mancanza di un termine migliore.
La seguente funzione avvolge una funzione asincrona esistente e crea una nuova funzione che regola automaticamente:
// This routine makes a custom throttle wrapper for the async-function that's passed in.
// A throttle function, when called, calls an async function (one that returns a promise)
// in such a way to ensure the async function is only called when it's not already in progress.
// in which case rather than calling it again, this routine returns the original promise to the caller.
// If it's already in progress, then the original promise is returned instead.
var makeThrottleFunction = function (asyncFunction) {
var asyncPromise = null;
return function (asyncInParam) {
if (!asyncPromise) {
var asyncDeferred = $q.defer();
asyncPromise = asyncDeferred.promise;
asyncFunction(asyncInParam).then(function (asyncOutParam) {
asyncDeferred.resolve(asyncOutParam);
}).catch(function (parameters) {
return $q.reject(parameters);
}).finally(function () {
asyncPromise = null;
});
}
return asyncPromise;
};
};
Esempio di utilizzo:
var getCurrentUser = makeThrottleFunction(function() {
// async call to server to get data
// return promise..
});
Sono curioso di cosa hanno da dire gli esperti su questo approccio. Che cosa è un'alternativa, ecc. L'ho usato dappertutto nella mia app e funziona davvero bene. Inoltre, non credo che potrei farne a meno, il che solleva la domanda: se qualcosa del genere non è già presente nei framework, allora potrei mancare qualcosa di ovvio, come qualche impostazione o libreria che già fa questa cosa esatta. Quindi mi piacerebbe avere un feedback.
Sebbene io stia cercando le opinioni degli altri su come risolvere questo problema comune, al fine di soddisfare questo essere un Q & A, penso che una buona risposta sarebbe una delle seguenti: 1. Indica una biblioteca che fa questo tipo di cose. 2. Sottolinea che esiste un modo più semplice per farlo.