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?
Ionic: 3.9.2
Angular: 5.0.3
TypeScript: 2.4.2
EDIT: I came across another problem with this which may be useful to some.
Changing the values of a binding in the constructor behaves differently to ionViewDidLoad or 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()
}
Async/Await is used to work with promises in asynchronous functions. It is basically syntactic sugar for promises. It is just a wrapper to restyle code and make promises easier to read and use. It makes asynchronous code look more like synchronous/procedural code, which is easier to understand.
Await is basically syntactic sugar for Promises. It makes your asynchronous code look more like synchronous/procedural code, which is easier for humans to understand.
This rule applies when the await operator is used on a non-Promise value. await operator pauses the execution of the current async function until the operand Promise is resolved.
I found out that running async-await can be much slower in some scenarios. But if I click on the 'both' button, the 'await' version is ~3-4 times slower than the promises version.
Angular relies on Zone.js for change detection, and Zone.js provides this by patching every API that can provide asynchronous behaviour.
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 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).
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.
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));
}
}
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With