在angularjs中解析非$q承诺后,自动应用$scope更改(触发摘要)

Automatically apply $scope changes (trigger digest) after a non-$q promise resolves in angularjs

本文关键字:更改 scope 应用 angularjs 承诺      更新时间:2023-09-26

假设您有:

function myPromise() {
  // using some promise lib other than $q, like Q or bluebird
  var defer = Q.defer();
  defer.resolve('John');
  return defer.promise;
}

然后你就有了这样的东西:

function foo() {
  return myPromise().then(function (name) {
    return name + ' Smith';
  });
}
foo().then(function (name) {
  $scope.$apply(function () { // or can use $timeout
    console.log('after foo');
    $scope.text = 'Hey ' + name;
  });
});

有没有一种方法可以修改foo,使foo块后面的自动包装在$scope中$apply()?即我们可以只拥有:

foo().then(function (name) {
  console.log('after foo');
  $scope.text = 'Hey ' + name;
  // $scope.$apply() is automatically called after this block is executed
});

我问这个问题的原因是,我正在开发一个API,用户将在其中进行几个foo调用,如果在foo promise解析后自动触发摘要循环,这将大大减少用户的代码。我知道我可以使用回调来实现这一点,但我希望有一种更干净的方法,只使用promise。

我的直觉是,解决方案在于将非$q承诺包装成$q承诺。。。

目前在Angular端没有办法做到这一点。从另一个方向看,Q promise库也没有为此暴露钩子。

所以,你有两个选择:

  • 覆盖所有Q非角度承诺的then方法,这将起作用,因为Q是如何结构的,但有点风险,并且可能在未来版本的Q中中断(所以在升级时要小心)
  • 使用像Bluebird这样的promise库,它可以让你像想要的那样无缝地使用Angular,而不会出现任何短片段问题。对于Bluebird,这个问题要简单得多

我强烈建议执行后者,并使用promise库来设置调度程序。它有更好的支持,不会产生过度的承诺。然而,如果你想使用第一个,这里是如何:

var oldThen = Q.Promise.prototype.then; // reference `then` 
Q.Promise.prototype.then = function(){
    return oldThen.apply(this, arguments). // decorate
           then(function(value){
                $rootScope.$evalAsync(function(){}); // schedule digest
                return value; // keep value
           },function(err){
               $rootScope.$evalAsync(function(){}); // schedule digest
               return $q.reject(err); // keep rejection
           });
};

基本上,如果每个Q在完成后没有安排,我们会在这里安排一个摘要。关于为什么这样做,你可以阅读这个问题和答案。

(信用通知-我从我参与的一次讨论中获得了上面的代码。上面的大部分代码都是我写的,但也有一些是Angular的Jeff Cross写的,所以也要归功于他)

我只需将API函数包装在$timeout块中,就可以触发摘要循环,例如

function foo() {
  return $timeout(function () {
    return myPromise().then(function (name) {
      return name + ' Smith';
    });
  });
}