一尘不染

AngularJS:$ q->延迟的API事物顺序(生命周期),谁调用摘要?

angularjs

$
Q服务
是angularjs非常强大,使我们的生活与异步代码更容易。

我对angular并不陌生,但是对我而言,使用延迟API并不是一个新手。我必须说,我完全同意How to use文档的部分内容+在文档中有非常有用的链接+我也检查了源代码。

我的问题是更多的 引擎盖下* 的角推迟和承诺API对象的部分。它们 生命周期 中的确切 阶段
是什么,以及它们如何相互作用。我的假设是,当诺言解决时,它会调用摘要循环?是/否?
*rootScope.Scope

能否针对以下方面提供详细的答案:

  1. 在您描述的每个步骤/阶段中发生的事情的顺序是什么
  2. 当创建具有新的Promise实例的新的延迟对象时,谁知道它/它重要吗?
  3. 解决承诺对象时,范围如何准确更新?我是否必须在回调中手动更新它,否则摘要将被自动调用并像这里声明的那样更新rootScope
  4. 提到至少一种从promise回调中更新范围的方法
  5. 我认为还有很多其他有用的方面,可以随时提供。

我将不胜感激并接受最详细的答案,并尽可能多地引用文档或源代码(我自己找不到)。我以前找不到与该主题相关的任何讨论(如果已有的话)-请发布链接。

ps:对于建议为该问题提供更好标题的人,可以+1,请在评论中添加您的建议。

干杯!


阅读 250

收藏
2020-07-04

共1个答案

一尘不染

承诺有三个状态

  • 待定 -这是诺言开始的方式。
  • 已实现 -当您解决了延期时,或者当from的返回值.then满足时,通常会发生这种情况,这类似于标准返回值。
  • 拒绝 -当您拒绝延期,throw.then处理程序或返回取消包装为拒绝的承诺时,会发生这种情况*,这通常类似于抛出标准异常。

在Angular中,promise异步解析,并通过解决via
$rootScope.$evalAsync(callback);(从此处获取)来提供其保证。

由于它是通过运行的,因此$evalAsync我们知道在诺言解决之后(正常情况下)至少会发生一个摘要周期,因为如果没有进行摘要,它将安排新的摘要。

这也是为什么例如当您要在Angular中对Promise 代码进行单元测试时,由于$
evalAsync执行是摘要循环的一部分,因此需要运行摘要循环(通常在rootScopevia上$rootScope.digest())。

好的,足够多的谈话,向我展示代码:

注意: 这显示了来自Angular 1.2的代码路径,Angular 1.x中的代码路径都相似,但在1.3+版本中,$
q已重构为使用原型继承,因此该答案在代码中不准确(但实际上)。这些版本。

1)创建$
q时,它会执行以下操作

  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
    return qFactory(function(callback) {
      $rootScope.$evalAsync(callback);
    }, $exceptionHandler);
  }];

依次执行以下操作:

function qFactory(nextTick, exceptionHandler) {

并且仅将解析nextTick通过作为$evalAsync内部解析并通知:

  resolve: function(val) {
    if (pending) {
      var callbacks = pending;
      pending = undefined;
      value = ref(val);

      if (callbacks.length) {
        nextTick(function() {
          var callback;
          for (var i = 0, ii = callbacks.length; i < ii; i++) {
            callback = callbacks[i];
            value.then(callback[0], callback[1], callback[2]);
          }
        });
      }
    }
  },

在根作用域上,$ evalAsync定义为:

  $evalAsync: function(expr) {
    // if we are outside of an $digest loop and this is the first time we are scheduling async
    // task also schedule async auto-flush
    if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
      $browser.defer(function() {
        if ($rootScope.$$asyncQueue.length) {
          $rootScope.$digest();
        }
      });
    }

    this.$$asyncQueue.push({scope: this, expression: expr});
  },

  $$postDigest : function(fn) {
    this.$$postDigestQueue.push(fn);
  },

如您所见,如果我们不在其中,并且以前未计划过摘要,那么您确实在计划摘要。然后将功能推到$$asyncQueue

反过来在$ digest内部(在一个循环中以及 测试观察者 之前 ):

 asyncQueue = this.$$asyncQueue,
 ...
 while(asyncQueue.length) {
      try {
          asyncTask = asyncQueue.shift();
          asyncTask.scope.$eval(asyncTask.expression);
      } catch (e) {
          clearPhase();
          $exceptionHandler(e);
      }
      lastDirtyWatch = null;
 }

因此,正如我们所看到的,它$$asyncQueue一直运行到,直到它为空,然后执行您的promise中的代码。

因此,正如我们所看到的,更新作用域只是分配给它,如果摘要尚未运行,则摘要将运行,如果是,则将$evalAsync在观察程序运行之前调用promise中运行的代码。如此简单:

myPromise().then(function(result){
    $scope.someName = result;
});

足够,保持简单。

*注意角度区分抛出与拒绝-默认情况下会记录抛出,并且必须明确记录拒绝

2020-07-04