【问题标题】:Accurately tracking Angular's active/done promises invoked by $q.defer准确跟踪 $q.defer 调用的 Angular 的 active/done 承诺
【发布时间】:2014-10-16 05:10:14
【问题描述】:

对于我的 Angular 应用程序,我正在尝试跟踪已处理的承诺数量以及仍需要处理的承诺数量。

我的代码用包装器方法装饰 $q,每当操作开始和完成时更新一个简单的计数器,看起来很简单:

  angular.module('DuckieTV',[])
  .config(function($provide) {
  var count = window.promiseStats = { 
    open: 0,
    done: 0 
  };
  $provide.decorator('$q', function($delegate) {

    function decorate(p) {
      p._then = p.then;
      p.then = function(thenFn, errFn, notifyFn) {
       count.open++;
       return p._then(function() { 
            count.done++;
            if(thenFn) return thenFn.apply(this,arguments)
        }, function() {
          count.done++;
          if(errFn) return errFn.apply(this,arguments)
        }, notifyFn);
      };

      p._finally = p.finally;
      p.finally = function(callback) {
        count.done++;
        p._finally(callback)
      }

      p._catch = p.catch;
      p.catch = function(callback) {
        count.done++;
        p._catch(callback)
      }
      return p;
    }

    var d = $delegate.defer;
    $delegate.defer = function() {
      var deferred = d();
      decorate(deferred.promise);
      return deferred;
    };

    return $delegate;
  });
})

当我注意到开始/完成的承诺之间存在差异时,乐趣就开始了。在执行大量延迟操作只需几分钟后,总体上它可以达到 > 15% 的百分比。

控制台输出示例:

promiseStats
Object {open: 99, done: 95}
Math.floor(promiseStats.done / promiseStats.open * 100);
94

经过一些工作(导入操作)

promiseStats;
Object {open: 185, done: 172}
Math.floor(promiseStats.done / promiseStats.open * 100);
92

我的实际问题:谁能告诉我这个实现是否遗漏了什么?

据我所知,我没有错过可选实现,并且我通过 .then 触发的所有承诺都已正确编码

【问题讨论】:

  • 谢谢@bergi,这可能会很有趣。
  • 我会把它变成答案

标签: javascript angularjs profiling promise angular-promise


【解决方案1】:

谁能告诉我这个实现是否遗漏了什么?

我可以看到您的实施存在两个问题:

  • 您装饰的catchinvoke 方法只会增加done 计数,永远不会增加open 计数。既然你没有看到这个,我猜你没有在你的代码中使用过。
  • 通过在每个.then() 调用中引入onfail 处理程序,您可以隐式捕获所有错误,并严重影响hurt control flow。您可以通过将以下行附加到您的处理程序代码来解决此问题:

     …
     else throw arguments[0];
    

我注意到已开始/已完成的承诺之间存在差异

我认为这不是由您实施计数器造成的。相反,您真的在您的代码中有一些未决的承诺;即从未解决的延迟。例如,这可能是由使用 deferred antipattern 的代码中的错误引起的。

我的代码用包装方法装饰$q,每当操作开始和完成时更新一个简单的计数器,看起来很简单:

事实上,每当一个监听器被附加时,它都会更新一个计数器,即一个then/catch/finally方法被调用。我会提出一个更简单的实现,它依赖于 creation,甚至不需要覆盖方法:

$provide.decorator('$q', function($delegate) {
    var defer = $delegate.defer;
    $delegate.defer = function() {
        var deferred = defer();
        count.open++;
        deferred.promise.finally(function() {
            count.done++;
        });
        return deferred;
    };
    return $delegate;
});

【讨论】:

  • 感谢您的出色回答。这就是我来到 StackOverflow 的目的。我肯定对滥用延迟反模式感到内疚,并会尽快解决这个问题:)
  • 所以它真的对未决的承诺负责?感谢您的确认,我只是怀疑会发生这种情况:-)
【解决方案2】:

情节变厚了。

我一直在根据 @Bergi 的优秀代码分析和跟踪代码,并注意到始终如一的承诺 #31 保持打开状态,因此我在此处添加了一个调试语句:

.config(function($provide) {
    var count = window.promiseStats = {
        open: 0,
        done: 0,
        promises: {}
    };
    $provide.decorator('$q', function($delegate) {


    var defer = $delegate.defer;
        $delegate.defer = function() {

            count.open++;
            var traceId = count.open;
            if(traceId == 31) { 
                debugger;
            }
            var deferred = count.promises[traceId] = defer();
            console.timeline('promise ' +traceId);
            console.profile('promise '+traceId);

            deferred.promise.finally(function() {
                count.done++;
                console.timelineEnd('promise ' +traceId);
                console.profileEnd('promise '+traceId);
                delete count.promises[traceId];    
            });
            return deferred;
        };
        return $delegate;
    });
})

这让我直接陷入了一个似乎有不同类型的承诺处理的 angular-core 模板请求中。

我仍在尝试评估这是否是一个问题。因为它似乎使用一些专门的代码取消注册。

【讨论】:

  • 好吧,there we've got them!在cachedRespreturned 的情况下,确实存在一个从未解析过的deferred。这不是一个真正的问题(因为没有人期望 deferred.promise 在这种情况下解决),但它看起来像不干净的编码。
猜你喜欢
  • 2015-05-18
  • 1970-01-01
  • 2013-07-11
  • 2015-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-21
  • 1970-01-01
相关资源
最近更新 更多