Almacenamiento en caché de un objeto de promesa en el servicio AngularJS
Quiero implementar una carga dinámica de un recurso estático en AngularJS usando Promises. El problema: tengo un par de componentes en la página que podrían (o no, depende de cuáles se muestren, por lo tanto dinámicos) necesitar obtener un recurso estático del servidor. Una vez cargado, se puede almacenar en caché durante toda la vida útil de la aplicación.
He implementado este mecanismo, pero soy nuevo en Angular y Promises, y quiero asegurarme de si esta es una solución/enfoque correcto.
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;
}
Por lo tanto, solo se realiza una solicitud y todas las siguientes llamadas a loadDataPromise() recuperan la primera promesa hecha. Parece funcionar para una solicitud que está en progreso o una que ya terminó hace algún tiempo.
¿Pero es una buena solución para almacenar en caché las promesas?
¿Es este el enfoque correcto?
Sí. El uso de la memorización de funciones que regresan promete una técnica común para evitar la ejecución repetida de tareas asincrónicas (y generalmente costosas). La promesa facilita el almacenamiento en caché porque no es necesario distinguir entre operaciones en curso y finalizadas; ambas se representan como (la misma) promesa para el valor del resultado.
¿Es esta la solución correcta?
No. Esa variable global data
y la resolución undefined
no es la forma en que se pretende que funcionen las promesas. En lugar de eso, ¡cumple la promesa con el resultado data
! También facilita mucho la codificación:
var dataPromise = null;
function getData() {
if (dataPromise == null)
dataPromise = $http.get("data.json").then(function (res) {
return res.data;
});
return dataPromise;
}
Entonces, en lugar de loadDataPromise().then(function() { /* use global */ data })
eso, es simplemente getData().then(function(data) { … })
.
Para mejorar aún más el patrón, es posible que desee ocultarlo dataPromise
en un alcance de cierre y observar que necesitará una búsqueda de diferentes promesas cuando getData
tome un parámetro (como la URL).
Para esta tarea creé un servicio llamado defer-cache-service que elimina todo este código de placa de caldera. Está escrito en Typecript, pero puede obtener un archivo js compilado. Código fuente de Github .
Ejemplo:
function loadCached() {
return deferCacheService.getDeferred('cacke.key1', function () {
return $http.get("data.json");
});
}
y consumir
loadCached().then(function(data) {
//...
});
Una cosa importante a tener en cuenta es que si digamos que dos o más partes llaman al mismo loadDataPromise y al mismo tiempo, debes agregar esta verificación
if (defer && defer.promise.$$state.status === 0) {
return defer.promise;
}
de lo contrario, realizará llamadas duplicadas al backend.