AngularJS:$q ->延迟 API 事物顺序(生命周期)以及谁调用摘要? [英] AngularJS : $q -> deferred API order of things (lifecycle) AND who invokes digest?

查看:25
本文介绍了AngularJS:$q ->延迟 API 事物顺序(生命周期)以及谁调用摘要?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

$q 服务在 angularjs 中非常强大,让我们的生活使用异步代码更容易.

The $q service is very powerful in angularjs and make our life easier with asynchronous code.

我是 angular 的新手,但使用延迟 API 对我来说并不陌生.我必须说我完全同意文档的 How to use 部分 + 文档中有非常有用的链接 + 我也查看了源代码.

I am new to angular but using deferred API is not very new to me. I must say that I completely ok with the How to use part of documentation + there are very useful links for that within the docs + I checked out the source either.

我的问题更多是关于 angular 中延迟和承诺 API 对象的幕后部分.生命周期中的确切阶段是什么?它们如何与rootScope.Scope(s)交互.我的假设是,当承诺解决时 - 它会调用摘要循环???是/否?

My question is more about the under the hood parts of deferred and promise API objects in angular. What are the exact phases in their life cycles and how are they interacts with rootScope.Scope(s). My assumptions are that when the promise resolves - it invokes the digest loop ??? yes / no ?

能否就以下几个方面提供具体的详细回答:

Can one provide a detailed answer with specific respect to the following list of aspects:

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

我将感谢并接受最详细的答案,并尽可能多地引用文档或来源(我自己找不到).我找不到任何以前与此主题的讨论,如果已经有的话 - 请发布链接.

I will appreciate and accept the most detailed answer, with as much as possible references to docs or source (that i couldn't find by myself). I can't find any previously discussion with this topic, if there already was - please post links.

ps:如果有人为这个问题提出更好的标题,请为任何人+1,请在评论中添加您的建议.

ps: +1 for any one that will help by suggesting a better title for this question, please add your suggestions in a comment.

干杯!

推荐答案

Promises 有三种状态

Promises have three states

  • 待定 - 这就是承诺的开始.
  • Fulfilled - 当您解析延迟时会发生这种情况,或者当来自 .then 的返回值满足时会发生这种情况,它通常类似于标准返回值.
  • Rejected - 当你拒绝一个 deferred、当你从 .then 处理程序中 throw 或者当你返回一个 promise 时会发生这种情况解包为拒绝*,通常类似于抛出的标准异常.
  • Pending - this is how promises start.
  • Fulfilled - this is what happens when you resolve a deferred, or when the return value from .then fulfills, and it generally analogous to a standard return value.
  • Rejected - This is what happens when you reject a deferred, when you throw from a .then handler or when you return a promise that unwraps to a rejection*, it is generally analogous to a standard exception thrown.

在 Angular 中,promise 异步解析并通过通过 $rootScope.$evalAsync(callback);(取自 此处).

In Angular, promises resolve asynchronously and provide their guarantees by resolving via $rootScope.$evalAsync(callback); (taken from here).

由于它是通过 $evalAsync 运行的,我们知道在 promise 解析后(通常)至少会发生一个摘要循环,因为如果没有在进行中,它将安排一个新的摘要.

Since it is run via $evalAsync we know that at least one digest cycle will happen after the promise resolves (normally), since it will schedule a new digest if one is not in progress.

这也是为什么例如当你想在 Angular 中对 promise 代码进行单元测试时,你需要运行一个摘要循环(通常,在 rootScope 上通过 $rootScope.digest()) 因为 $evalAsync 执行是摘要循环的一部分.

This is also why for example when you want to unit test promise code in Angular, you need to run a digest loop (generally, on rootScope via $rootScope.digest()) since $evalAsync execution is part of the digest loop.

注意:这显示了来自 Angular 1.2 的代码路径,Angular 1.x 中的代码路径都是相似的,但在 1.3+ 中 $q 已被重构为使用原型继承,所以这个答案不是这些版本的代码准确(但在精神上).

Note: This shows the code paths from Angular 1.2, the code paths in Angular 1.x are all similar but in 1.3+ $q has been refactored to use prototypical inheritance so this answer is not accurate in code (but is in spirit) for those versions.

1) 当 $q 被创建时,它会这个:

1) When $q is created it does this:

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

反过来,这样做:

function qFactory(nextTick, exceptionHandler) {

并且仅解析在解析和通知中作为 $evalAsync 传递的 nextTick :

And only resolves on nextTick passed as $evalAsync inside resolve and notify:

  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 定义为:

On the root scope, $evalAsync is defined as:

  $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.

Which, as you can see indeed schedules a digest if we are not in one and no digest has previously been scheduled. Then it pushes the function to the $$asyncQueue.

依次在 $digest 内(在一个循环中,以及之前测试观察者):

In turn inside $digest (during a cycle, and before testing the watchers):

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

所以,如我们所见,它在 $$asyncQueue 上运行,直到它为空,执行您的 Promise 中的代码.

So, as we can see, it runs on the $$asyncQueue until it's empty, executing the code in your promise.

所以,正如我们所看到的,更新范围只是简单地分配给它,如果它尚未运行,则摘要将运行,如果是,则承诺中的代码在 $evalAsync 在观察者运行之前被调用.这么简单:

So, as we can see, updating the scope is simply assigning to it, a digest will run if it's not already running, and if it is, the code inside the promise, run on $evalAsync is called before the watchers are run. So a simple:

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

足够了,保持简单.

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

这篇关于AngularJS:$q ->延迟 API 事物顺序(生命周期)以及谁调用摘要?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆