【问题标题】:Angular data binding won't work with async/await, yet it will with promisesAngular 数据绑定不适用于 async/await,但它适用于 Promise
【发布时间】:2018-08-23 08:37:10
【问题描述】:

如果在 await 语句之后更改了数据绑定的值,则不会更新数据绑定。

  handle() {
    this.message = 'Works'
  }

  async handle() {
    this.message = 'Works'
  }

  async handle() {
    await new Promise((resolve, reject) => {
      resolve()
    })
    this.message = 'Works'
  }

  async handle() {
    await new Promise((resolve, reject) => {
      setTimeout(() => resolve(), 3000)
    })
    this.message = 'Doesn\'t work'
  }

  handle() {
    new Promise((resolve, reject) => {
      setTimeout(() => resolve(), 3000)
    })
    .then(() => this.message = 'Works')
  }

为什么最后两个行为不一样?他们不应该是同一件事吗?

离子:3.9.2

角度:5.0.3

TypeScript:2.4.2

编辑:我遇到了另一个可能对某些人有用的问题。

在构造函数中更改绑定的值与 ionViewDidLoad 或 ngOnInit 的行为不同!

  constructor(private zone: NgZone) {
    // This will cause the same problems, bindings not updating
    this.handle()
  }

  constructor(private zone: NgZone) {
    // Unless you do this...
    this.zone.run(() => {
      this.handle()
    })
  }

  ionViewDidLoad() {
    // But I think this is better/cleaner
    this.handle()
  }

【问题讨论】:

  • @Jeto 它适用于console.log,问题是如果您在具有数据绑定的组件中尝试它,则值不会更新
  • 那你能用它构建一个最小的 StackBlitz 吗?
  • 我在试,之前没听说过:)
  • 我似乎无法在那里重现它...我的包是不同的版本,我也无法设置 tsconfig。

标签: javascript angular typescript data-binding async-await


【解决方案1】:

这与变化检测在 Angular 中的工作方式有关。看: https://stackblitz.com/edit/angular-jajbza?file=app%2Fapp.component.ts

我打赌 Ionic 默认使用 OnPush 策略,或者您已启用它,就像我在 Blitz 中所做的那样。一切都很好,IMO 无论如何都应该默认启用它,因为它会迫使您考虑这些事情并编写更高性能的代码。

无法明确说明为什么在您调用 .then 时在上一个示例中更新了您的视图。也许在那种情况下,CD 设法跟踪 Promise,但不能使用异步函数。如果你不这样做,异步函数本身会返回一个 Promise,所以基本上在编译之后,async handle() 返回类似 Promise.resolve(null) 的东西,尽管实际的 JS 代码可能看起来比这更混乱。

编辑:另一种可能比手动调用 detectChanges 更干净的方法是运行任何改变 Angular 区域内视图的东西:

import { NgZone } from '@angular/core';
constructor (private zone:NgZone){}

// after await somePromise :
this.zone.run(() => { 
    this.someProperty = 'Something changed';
});

Edit2:有趣的是,zone.run() 实际上并没有改变任何东西。在闪电战中进行了测试。所以手动CD是唯一的方法。避免 async/await 并尝试坚持使用 Observables 和 NG 的异步管道的另一个原因。 :)

【讨论】:

  • 现在我必须将我的 async/await 代码恢复为 Promise,以便不必做这些类型的事情。尽管这里的一切都完全正常:stackblitz.com/edit/ionic-g8d9fu,但由于某种原因它在我的环境中不起作用......我什至明确设置了 changeDetection:ChangeDetectionStrategy.Default。有什么方法可以让它在我的环境中工作?
  • 我认为可能是转译问题
  • 这与离子有关。我没有使用它,但听说它在使用默认策略时会导致类似 OnPush 的效果。 Electron 也发生在我身上,所以关于这些环境的某些事情有时会导致更改检测“不仅仅是工作”。如果我是你,我会在这些异步函数中添加 cdRef.markForCheck() 以使其快速运行。 =)
  • .. markForCheck() 而不是 detectChanges() 即使它可能会导致更多的开销,因为如果你碰巧在视图被破坏后调用它,detectChanges 可能会导致“你试图做某事”组件在此期间已被销毁。所以更安全:)
  • 有趣的是,我实际上在使用电子
【解决方案2】:

检查您的浏览器版本及其支持的内容,async/await 是 ES2017 原生的。如果您的浏览器不支持,请使用 ES2016 作为您的目标。

我需要用于 Electron 的 ES2016。

【讨论】:

  • 回答我自己的问题 - 虽然我不能接受它作为另外两天的答案......
【解决方案3】:

Angular 依赖 Zone.js 进行变更检测,Zone.js 通过修补每个可以提供异步行为的 API 来提供这一点。

问题在于原生async 函数是如何实现的。正如this question 所证实的那样,它们不仅仅围绕全局Promise,而是依赖于可能因浏览器而异的内部机制。

Zone.js 修补了Promise,但不可能修补当前引擎实现中async 函数使用的内部承诺(这里是open issue)。

通常(async () => {})() instanceof Promise === true。对于 Zone.js,这是不正确的; async 函数返回本机 Promise 的实例,而 Promise global 是由 Zone.js 修补的区域感知承诺。

为了使原生 async 函数在 Angular 中工作,应该额外触发更改检测。这可以通过显式触发它(正如另一个答案已经暗示的那样)或使用任何区域感知 API 来完成。使用区域感知承诺包装 async 函数结果的助手可以解决问题:

function nativeAsync(target, method, descriptor) {
  const originalMethod = target[method];
  descriptor.value = function () {
    return Promise.resolve(originalMethod.apply(this, arguments));
  }
}

Here 是一个在async 方法上使用@nativeAsync 装饰器来触发更改检测的示例:

  @nativeAsync
  async getFoo() {
    await new Promise(resolve => setTimeout(resolve, 100));
    this.foo = 'foo';
  }

Here 是相同的示例,它没有使用额外的措施来触发变更检测,并且预期不会按预期工作。

在不需要转译步骤的环境中坚持本机实现是有意义的。由于Angular应用程序应该以任何方式编译,因此可以通过从ES2017切换到ES2015ES2016TypeScripttarget来解决问题。

【讨论】:

  • 您能否提供一个在 Angular 应用程序上工作的示例?这会很有帮助:)
【解决方案4】:

就像 estus 说的,目前zone.js 不支持原生 async/await,所以你不能编译针对ES2017 的打字稿,我正在努力,https://github.com/angular/zone.js/pull/795,我做了一个可以在nodejs中运行的工作演示,但是在浏览器(chrome)中,它仍然需要一些时间,因为chrome现在不打开javascript版本的AsyncHooks和PromiseHooks。

【讨论】:

    猜你喜欢
    • 2018-07-10
    • 2019-10-24
    • 2015-09-26
    • 2019-04-12
    • 2012-12-06
    • 2018-03-24
    • 1970-01-01
    • 2019-05-08
    • 2019-02-27
    相关资源
    最近更新 更多