Je souhaite implémenter un chargement dynamique d'une ressource statique dans AngularJS à l'aide de Promises. Le problème: sur la page, j'ai quelques composants qui pourraient (ou non, dépend de ce qui est affiché, donc dynamique) avoir besoin d’obtenir une ressource statique du serveur. Une fois chargé, il peut être mis en cache pendant toute la vie de l'application.
J'ai implémenté ce mécanisme, mais Angular et Promises sont nouveaux pour moi, et je veux m'assurer que cette solution est la bonne\approche.
var data = null;
var deferredLoadData = null;
function loadDataPromise() {
if (deferredLoadData !== null)
return deferredLoadData.promise;
deferredLoadData = $q.defer();
$http.get("data.json").then(function (res) {
data = res.data;
return deferredLoadData.resolve();
}, function (res) {
return deferredLoadData.reject();
});
return deferredLoadData.promise;
}
Ainsi, une seule demande est faite et tous les prochains appels à loadDataPromise () récupèrent la première promesse faite. Cela semble fonctionner pour les demandes qui sont en cours ou déjà terminées il y a quelque temps.
Mais est-ce une bonne solution pour cacher les promesses?
Est-ce la bonne approche?
Oui. L'utilisation des fonctions memoisation on qui retournent promet une technique commune permettant d'éviter l'exécution répétée de tâches asynchrones (et généralement coûteuses). La promesse facilite la mise en cache car il n’est pas nécessaire de faire la distinction entre les opérations en cours et les opérations terminées, elles sont toutes deux représentées comme (la même) promesse pour la valeur du résultat.
Est-ce la bonne solution?
Non. Cette variable globale data
et la résolution avec undefined
ne correspondent pas à la manière dont les promesses sont censées fonctionner. Remplissez plutôt la promesse avec le résultat data
! Cela facilite aussi beaucoup le codage:
var dataPromise = null;
function getData() {
if (dataPromise == null)
dataPromise = $http.get("data.json").then(function (res) {
return res.data;
});
return dataPromise;
}
Ensuite, au lieu de loadDataPromise().then(function() { /* use global */ data })
, il s’agit simplement de getData().then(function(data) { … })
.
Pour améliorer davantage le modèle, vous pouvez masquer dataPromise
dans une étendue de fermeture et noter que vous aurez besoin d'une recherche pour différentes promesses lorsque getData
prendra un paramètre (comme l'url).
Pour cette tâche, j'ai créé un service appelé defer-cache-service qui supprime tout le code de cette plaque de chaudière. Il a écrit en TypeScript, mais vous pouvez récupérer le fichier js compilé. Github code source .
Exemple:
function loadCached() {
return deferCacheService.getDeferred('cacke.key1', function () {
return $http.get("data.json");
});
}
et consommer
loadCached().then(function(data) {
//...
});
Il est important de noter que si deux parties ou plus appelant le même loadDataPromise et simultanément, vous devez ajouter cette vérification
if (defer && defer.promise.$$state.status === 0) {
return defer.promise;
}
sinon, vous ferez des appels en double au backend.
Ce modèle de conception utilisera cache tout ce qui est renvoyé lors de sa première exécution et renverra l'élément cached chaque fois qu'il sera appelé à nouveau.
const asyncTask = (cache => {
return function(){
// when called first time, put the promise in the "cache" variable
if( !cache ){
cache = new Promise(function(resolve, reject){
setTimeout(() => {
resolve('foo');
}, 2000);
});
}
return cache;
}
})();
asyncTask().then(console.log);
asyncTask().then(console.log);
Enveloppez simplement votre fonction avec une autre fonction auto-invoquante qui retourne une fonction (votre fonction asynchrone d'origine). Le but de la fonction wrapper est de fournir une étendue d'encapsulation pour une variable locale cache
, de sorte que cette variable locale ne soit accessible que dans la fonction retournée. la fonction wrapper et a exactement la même valeur chaque fois que asyncTask
est appelé (autre que la toute première fois)