35

我想使用 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() 的所有调用都会取回第一个做出的承诺。它似乎适用于正在进行的请求或前一段时间已经完成的请求。

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

4

3 回答 3

48

这是正确的方法吗?

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

这是正确的解决方案吗?

不,那个全局data变量和解决方案undefined并不是承诺的工作方式。相反,用结果来兑现承诺data!它还使编码变得更加容易:

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。

于 2013-09-11T15:35:51.767 回答
3

对于这个任务,我创建了名为 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;
}

否则您将对后端进行重复调用。

于 2015-11-30T14:36:30.517 回答
0

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

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(第一次除外)

于 2018-10-04T07:46:56.860 回答