14

我目前正在轮询服务器以检查新数据,然后相应地更新 AngularJS 应用程序中的模型。他大致就是我正在做的事情:

setInterval(function () {
    $http.get('data.json').then(function (result) {
        if (result.data.length > 0) {
          // if data, update model here
        } else {
          // nothing has changed, but AngularJS will still start the digest cycle
        }
    });
}, 5000);

这工作正常,但大多数请求不会导致任何新数据或数据更改,但 $http 服务并不真正知道/关心,仍然会触发摘要循环。我觉得这是不必要的(因为摘要循环是应用程序中最繁重的操作之一)。有没有办法仍然可以使用 $http 但如果没有任何变化,以某种方式跳过摘要?

一种解决方案是不使用 $http 而是使用 jQuery,然后调用 $apply 让 Angular 知道模型已更改:

setInterval(function () {
    $.get('data.json', function (dataList) {
        if (dataList.length > 0) {
            // if data, update model
            $scope.value = dataList[0].value + ' ' + new Date();

            // notify angular manually that the model has changed.
            $rootScope.$apply();
        }
    });
}, 5000);

虽然这似乎可行,但我不确定这是一个好主意。如果可能的话,我仍然想使用纯 Angular。

有人对上述方法的改进或更优雅的解决方案有任何建议吗?

PS 我使用 setInterval 而不是 $timeout 的原因是因为 $timeout 还会触发一个摘要循环,在这种情况下这将是不必要的,只会增加“问题”。

4

3 回答 3

8
  1. AngularJS @doc 提供的解决方案

AngularJS 建议使用一种 PERF 技巧,通过 $httpProvider 将几个 $http 响应捆绑在一个 $digest 中。这又不是解决问题,它只是一种镇静剂:)

$httpProvider.useApplyAsync(true)

  1. 保存 $$watchers 解决方案 - 有风险且不可扩展

首先,公认的解决方案是不可扩展的——你不可能在 100K 行 JS 代码项目上使用 $watchers 技巧——这是不可能的。

其次,即使项目很小,风险也很大!例如,如果另一个真正需要这些观察者的 ajax 调用到达,会发生什么?


  1. 另一个(可行的)有风险的解决方案

在不修改 AngularJS 代码的情况下实现此目的的唯一替代方法是将 $rootScope.$$phase 设置为 true 或 '$digest',进行 $http 调用,然后将 $rootScope.$$phase 设置为 null。

 $rootScope.$$phase = true;
 $http({...})
   .then(successcb, failurecb)
   .finally(function () {
       $rootScope.$$phase = null;            
   });

风险

1) 其他 ajax 调用可能会尝试做同样的事情 --> 它们需要通过包装 ajax 服务进行同步(通过 $http)

2) 用户可以在两者之间触发 UI 操作,将 $$phase 更改为 null 以及 ajax 调用何时返回的操作,并且仍然触发 $digest

扫描 AngularJS 源代码后弹出解决方案 - 这是保存情况的行:https ://github.com/angular/angular.js/blob/e5e0884eaf2a37e4588d917e008e45f5b3ed4479/src/ng/http.js#L1272


  1. 理想的解决方案

因为这是每个人在使用 AngularJS 时都会遇到的问题,所以我认为需要系统地解决它。上面的答案并没有解决问题,只是试图避免它。 所以我们应该创建一个 AngularJS 拉取请求,它允许我们通过 $httpProvider 指定一个不会触发特定 $http 请求摘要的配置。希望他们同意这需要以某种方式解决。

于 2015-11-24T19:02:37.043 回答
1

Web sockets would seem to be the most elegant solution here. That way you don't need to poll the server. The server can tell your app when data or anything has changed.

于 2014-02-21T18:31:41.443 回答
1

你可以通过这个技巧来做到这一点:

var watchers;

scope.$on('suspend', function () {
  watchers = scope.$$watchers;
  scope.$$watchers = [];
});

scope.$on('resume', function () {
  scope.$$watchers = watchers;
  watchers = null;
});

有了这个,您将删除您的范围或在 $digest 循环中重新插入它。

当然,您必须管理事件才能做到这一点。

参考这篇文章:

从摘要循环中删除和恢复范围

希望能帮助到你 !

于 2014-02-21T13:36:51.730 回答