Typescript async/await不会更新AngularJS视图

mon*_*oh_ 26 angularjs typescript angularjs-scope

我正在使用Typescript 2.1(开发人员版本)将async/await转换为ES5.

我注意到在我更改了在异步函数中绑定查看的任何属性后,视图不会使用当前值更新,所以每次我必须在函数结束时调用$ scope.$ apply().

示例异步代码:

async testAsync() {
     await this.$timeout(2000);
     this.text = "Changed";
     //$scope.$apply(); <-- would like to omit this
}
Run Code Online (Sandbox Code Playgroud)

此后text,视图中不会显示新值.

是否有任何解决方法,所以我不必每次手动调用$ scope.$ apply()?

Chr*_*ris 10

这里的答案是正确的,因为AngularJS不知道该方法,因此您需要"告诉"Angular有关已更新的任何值.

就个人而言,我会使用$q异步行为而不是使用await它作为"Angular方式".

你可以很容易地用$ q包装非Angular方法,即[注意这是我如何包装所有Google Maps函数,因为它们都遵循传递回调的模式以通知完成]

function doAThing()
{
    var defer = $q.defer();
    // Note that this method takes a `parameter` and a callback function
    someMethod(parameter, (someValue) => {
        $q.resolve(someValue)
    });

    return defer.promise;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它

this.doAThing().then(someValue => {
    this.memberValue = someValue;
});
Run Code Online (Sandbox Code Playgroud)

但是,如果您确实希望继续await使用$apply,那么在这种情况下,有一种比使用更好的方法$digest.像这样

async testAsync() {
   await this.$timeout(2000);
   this.text = "Changed";
   $scope.$digest(); <-- This is now much faster :)
}
Run Code Online (Sandbox Code Playgroud)

$scope.$digest在这种情况下,因为更好的$scope.$apply将执行脏检查(用于变化检测Angulars法)对所有范围所有绑定值,这可能是昂贵的性能明智的-特别是如果你有很多的绑定.$scope.$digest但是,它只会检查当前的绑定值,$scope使其更具性能.

  • 我不认为这是一种先进的技术,即使它肯定分享知识是一件好事吗?我的一般经验法则是:如果您只想影响当前范围,请使用`$ digest`,否则使用`$ apply`.将此技术应用于我的应用程序可以大大提高性能. (4认同)

Est*_*ask 7

这可以通过angular-async-await扩展方便地完成:

class SomeController {
  constructor($async) {
    this.testAsync = $async(this.testAsync.bind(this));
  }

  async testAsync() { ... }
}
Run Code Online (Sandbox Code Playgroud)

可以看出,它所做的只是使用$rootScope.$apply()随后调用的包装器包装promise-returns函数.

没有可靠的方法在async函数上自动触发摘要,这样做会导致黑客攻击框架和Promise实现.没有办法为本机async函数(TypeScript es2017目标)执行此操作,因为它依赖于内部承诺实现而不是Promise全局.更重要的是,这种方式是不可接受的,因为这不是默认情况下预期的行为.开发人员应完全控制它并明确指定此行为.

鉴于testAsync被多次调用,并且它被调用的唯一地方是testsAsync,自动摘要testAsync最终将导致消化垃圾邮件.虽然正确的方法是在之后触发一次摘要testsAsync.

在这种情况下$async,仅适用于testsAsync而不适用于testAsync自身:

class SomeController {
  constructor($async) {
    this.testsAsync = $async(this.testsAsync.bind(this));
  }

  private async testAsync() { ... }

  async testsAsync() {
    await Promise.all([this.testAsync(1), this.testAsync(2), ...]);
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)


ima*_*a97 5

我已经检查了angular-async-await的代码,似乎他们正在使用$rootScope.$apply()异步承诺解决后来消化表达式。

这不是一个好方法。您可以使用 AngularJS 原版,$q通过一个小技巧,您可以达到最佳性能。

首先,创建一个函数(例如,工厂,方法)

// inject $q ...
const resolver=(asyncFunc)=>{
    const deferred = $q.defer();
    asyncFunc()
      .then(deferred.resolve)
      .catch(deferred.reject);
    return deferred.promise;
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以在您的实例服务中使用它。

getUserInfo=()=>{

  return resolver(async()=>{

    const userInfo=await fetch(...);
    const userAddress= await fetch (...);

    return {userInfo,userAddress};
  });
};
Run Code Online (Sandbox Code Playgroud)

这与使用 AngularJS$q和最少的代码一样有效。


bas*_*rat 0

有没有任何解决方法,这样我就不必每次都手动调用 $scope.$apply() ?

这是因为 TypeScript 使用浏览器本机 Promise实现,而这不是 Angular 1.x 所了解的。要对其不控制的所有异步函数进行脏检查,必须触发摘要周期。