如何在AngularJS中测量请求(XHR)持续时间?

时间:2016-12-30 06:57:37

标签: javascript angularjs xmlhttprequest navigation-timing-api resource-timing-api

我正在尝试为AngularJS(1.5.x)实施一个拦截器,它可以测量每个api调用并将每个api调用的持续时间发送到Google Analytics。

我开始使用new Date().getTime()

的虚拟实现
(function (angular) {
  'use strict';

  angular.module('MODULE')
    .factory('HttpLoadingInterceptor', HttpLoadingInterceptor)
    .config(HttpLoadingInterceptorConfig);

  HttpLoadingInterceptor.$inject = ['$injector', '$q', 'AnalyticsService', '_'];

  HttpLoadingInterceptorConfig.$inject = ['$httpProvider'];

  function HttpLoadingInterceptor ($injector, $q, AnalyticsService, _) {
    var REQUEST_GA_EVENT_NAME = 'REQUEST';

    return {
      request: request,
      requestError: requestError,
      response: response,
      responseError: responseError
    };

    function request (config) {
      config.requestTimestamp = now();
      return config || $q.when(config);
    }

    function requestError (rejection) {
      rejection.config.responseTimestamp = now();
      trackRequest(rejection);
      return $q.reject(rejection);
    }

    function response (response) {
      response.config.responseTimestamp = now();
      trackRequest(response);
      return response || $q.when(response);
    }

    function responseError (rejection) {
      rejection.config.responseTimestamp = now();
      trackRequest(rejection);
      return $q.reject(rejection);
    }

    function trackRequest (response) {
      if (!_.startsWith(response.config.url, 'api')) {
        return;
      }
      AnalyticsService.trackEvent(
        REQUEST_GA_EVENT_NAME,
        response.config.url,
        response.status,
        response.config.responseTimestamp - response.config.requestTimestamp
      );
    }

    function now () {
      return new Date().getTime();
    }
  }

  function HttpLoadingInterceptorConfig ($httpProvider) {
    $httpProvider.interceptors.push('HttpLoadingInterceptor');
  }
})(window.angular);

乍看之下看起来不错,但是比较Chrome中网络标签>中收集的请求的持续时间,我发现在我的代码中收集的请求总是更高(有时很多!)来自Chrome收集的那些。

我想到的另一个想法是使用用户导航API ,因此我将代码更改为:

(function (angular, performance) {
  'use strict';

  angular.module('MODULE')
    .factory('HttpLoadingInterceptor', HttpLoadingInterceptor)
    .config(HttpLoadingInterceptorConfig);

  HttpLoadingInterceptor.$inject = ['$injector', '$q', 'AnalyticsService', '_'];

  HttpLoadingInterceptorConfig.$inject = ['$httpProvider'];

  function HttpLoadingInterceptor ($injector, $q, AnalyticsService, _) {
    var REQUEST_GA_EVENT_NAME = 'REQUEST';
    var measureReqCnt = 1;

    return {
      request: request,
      requestError: requestError,
      response: response,
      responseError: responseError
    };

    function request (config) {
      if (shouldMeasure(config.url)) {
        measureRequest(config);
      }
      return config || $q.when(config);
    }

    function requestError (rejection) {
      if (shouldMeasure(rejection.config.url)) {
        trackRequest(rejection);
      }
      return $q.reject(rejection);
    }

    function response (response) {
      if (shouldMeasure(response.config.url)) {
        trackRequest(response);
      }
      return response || $q.when(response);
    }

    function responseError (rejection) {
      if (shouldMeasure(rejection.config.url)) {
        trackRequest(rejection);
      }
      return $q.reject(rejection);
    }

    function shouldMeasure (url) {
      return performance && _.startsWith(url, 'api');
    }

    function measureRequest (config) {
      config.measureName = [config.url, measureReqCnt++].join('_');
      config.markStartName = [config.measureName, 'start'].join('_');
      config.markEndName = [config.measureName, 'end'].join('_');
      performance.mark(config.markStartName);
    }

    function trackRequest (response) {
      performance.mark(response.config.markEndName);
      performance.measure(response.config.measureName, response.config.markStartName, response.config.markEndName);
      var entries = performance.getEntriesByName(response.config.measureName, 'measure');
      if (entries && entries.length) {
        AnalyticsService.trackEvent(
          REQUEST_GA_EVENT_NAME,
          response.config.url,
          response.status,
          entries[0].duration
        );
      }
    }
  }

  function HttpLoadingInterceptorConfig ($httpProvider) {
    $httpProvider.interceptors.push('HttpLoadingInterceptor');
  }
})(window.angular, window.performance);

..但我再次收到的结果与 Chrome 收集的结果不同,甚至不是使用new Date().getTime()

测量的结果

我做错了什么?我该怎么做?也许Resource Timing API? AngularJS当然有点强加。

我无法使用用户导航API方法 - window.performacne.getEntries()来查找我的请求持续时间,因为我无法识别特定请求。每个请求的参数都不可用,我有很多相同的API调用只有不同的参数。

我应该装饰AngularJS使用的原生XHR请求吗?

1 个答案:

答案 0 :(得分:0)

如何利用performance.now()? (https://developer.mozilla.org/en-US/docs/Web/API/Performance/now),与仅Date()相比,专为高精度性能跟踪而设计?

请参阅: performance.now() vs Date.now()

另外,你关心绝对数字吗?对于性能跟踪,有时相对是最好的,因为将苹果与橙子进行比较很少会产生真正的数据点。

我的猜测是,在使用什么计时器和时间点之间存在差异。 Chrome Dev工具可能更接近电线"而不是包括应用程序代码。如果它有帮助,我们还有一个API分析和调试工具(www.moesif.com/features),但完全披露我是CEO。