Angular数据绑定不适用于async/await,但适用于promises [英] Angular data binding won't work with async/await, yet it will with promises

查看:299
本文介绍了Angular数据绑定不适用于async/await,但适用于promises的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Data bindings don't get updated if their values are changed after an await statement.

  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')
  }

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

Why do the last two not behave the same? aren't they supposed to be the same thing?

离子:3.9.2

角度:5.0.3

TypeScript:2.4.2

TypeScript: 2.4.2

我遇到了另一个问题,可能对某些人有用.

I came across another problem with this which may be useful to some.

在构造函数中更改绑定的值的行为与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()
  }

推荐答案

Angular依赖Zone.js进行更改检测,而Zone.js通过修补可以提供异步行为的每个API来提供此功能.

Angular relies on Zone.js for change detection, and Zone.js provides this by patching every API that can provide asynchronous behaviour.

问题在于如何实现本机async函数.正如此问题中所确认的那样,它们不仅环绕全局Promise,而且依赖内部机制可能因浏览器而异.

The problem is in how native async functions are implemented. As confirmed in this question, they don't just wrap around global Promise but rely on internal mechanisms that may vary from one browser to another.

Zone.js修补了Promise,但是不可能修补当前引擎实现中由async功能使用的内部承诺(此处为

Zone.js patches Promise but it's impossible to patch internal promise that is used by async functions in current engine implementations (here is open issue for that).

通常是(async () => {})() instanceof Promise === true.对于Zone.js,情况并非如此; async函数返回本地Promise的实例,而Promise global则是Zone.js修补的区域感知承诺.

Usually (async () => {})() instanceof Promise === true. In case of Zone.js, this isn't true; async function returns an instance of native Promise, while Promise global is zone-aware promise patched by Zone.js.

为了使本机async函数在Angular中工作,应该另外触发更改检测.可以通过显式触发它(如另一个答案已经建议的那样)或使用任何可识别区域的API来完成.一个将async函数结果与区域感知的承诺包装在一起的助手将达到目的:

In order to make native async functions work in Angular, change detection should be additionally triggered. This can be done by triggering it explicitly (as another answer already suggests) or by using any zone-aware API. A helper that wraps async function result with zone-aware promise will do the trick:

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

此处是在async方法上使用@nativeAsync装饰器的示例触发变更检测:

Here is an example that uses @nativeAsync decorator on async methods to trigger change detection:

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

此处是相同的示例,它没有使用其他措施来触发变更检测并预期无法正常工作.

Here is same example that doesn't use additional measures to trigger change detection and expectedly doesn't work as intended.

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

It makes sense to stick to native implementation in environment that doesn't require transpilation step. Since Angular application is supposed to be compiled any way, the problem can be solved by switching from ES2017 to ES2015 or ES2016 TypeScript target.

这篇关于Angular数据绑定不适用于async/await,但适用于promises的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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