在 AngularJS 服务中缓存Promise对象

IT技术 javascript angularjs promise angular-promise
2021-01-28 10:45:43

我想使用 Promises 在 AngularJS 中实现静态资源的动态加载。问题:我在页面上有几个组件可能(或不,取决于显示的内容,因此是动态的)需要从服务器获取静态资源。加载后,它可以在整个应用程序生命周期中缓存。

我已经实现了这个机制,但我是 Angular 和 Promises 的新手,我想确定这是否是一个正确的解决方案\方法。

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;
}

因此,只发出一个请求,所有对 loadDataPromise() 的下一次调用都会返回第一个做出的Promise。它似乎适用于正在进行的请求或前一段时间已经完成的请求。

但它是缓存 Promise 的好方法吗?

3个回答

这是正确的方法吗?

是的。在返回的函数上使用记忆化保证了一种避免重复执行异步(通常是昂贵的)任务的通用技术。Promise使缓存变得容易,因为不需要区分正在进行的和已完成的操作,它们都表示为结果值的(相同)Promise。

这是正确的解决方案吗?

不。那个全局data变量和解析undefined不是Promise的工作方式。相反,用结果履行Promisedata它还使编码更容易:

var dataPromise = null;

function getData() {
    if (dataPromise == null)
        dataPromise = $http.get("data.json").then(function (res) {
           return res.data;
        });
    return dataPromise;
}

然后,而不是loadDataPromise().then(function() { /* use global */ data })简单地getData().then(function(data) { … }).

为了进一步改进该模式,您可能希望隐藏dataPromise在一个闭包作用域中,并注意到当getData接受一个参数(如 url)时,您将需要查找不同的 Promise。

@Darryl:抱歉,我对 Angular 了解不多,我的回答仅基于Promise。
2021-03-14 10:45:43
为什么不使用 $cacheFactory?这不是给从整个module访问缓存提供了更大的灵活性吗?
2021-03-21 10:45:43
@Darryl:我想主要区别在于,通过使用 $cacheFactory 缓存服务调用的结果,而通过“dataPromise”缓存Promise本身。第二种方式,如果调用服务两次,第一次调用还没有返回,则不会触发第二次调用,当第一次调用返回时,两个promise都解决了。这在某些情况下是一个很大的优势
2021-03-28 10:45:43
你最后为我节省了很多冗余和错误的代码
2021-04-09 10:45:43

对于这个任务,我创建了一个名为 defer-cache-service 的服务,它删除了所有这些样板代码。它是用 Typescript 编写的,但您可以抓取编译后的 js 文件。Github 源代码

例子:

function loadCached() {
   return deferCacheService.getDeferred('cacke.key1', function () {
      return $http.get("data.json");
   }); 
} 

并消耗

loadCached().then(function(data) {
//...
});

需要注意的一件重要事情是,如果让我们说两个或更多部分同时调用相同的 loadDataPromise,则必须添加此检查

if (defer && defer.promise.$$state.status === 0) {
   return defer.promise;
}

否则你将重复调用后端。

这种设计设计模式将缓存第一次运行时返回的内容,并在每次再次调用时返回缓存的内容。

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);

解释:

简单地用另一个自调用函数包装你的函数,它返回一个函数(你原来的异步函数),包装函数的目的是为局部变量提供封装范围cache,这样局部变量只能在返回的函数内访问包装函数并且每次asyncTask调用具有完全相同的值(第一次除外)