Angular数据绑定不适用于async/await,但适用于promises [英] Angular data binding won't work with async/await, yet it will with 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
切换到ES2015
或ES2016
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屋!