Skip to content Skip to sidebar Skip to footer

Angular Data Binding Won't Work With Async/await, Yet It Will With Promises

Data bindings don't get updated if their values are changed after an await statement. handle() { this.message = 'Works' } async handle() { this.message = 'Works' }

Solution 1:

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:

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

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

@nativeAsyncasyncgetFoo() {
    awaitnewPromise(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.

Solution 2:

just like estus said, currently zone.js don't support native async/await, so you can't compile typescript which target to ES2017, and I am working on it, https://github.com/angular/zone.js/pull/795, I have made a working demo which can run in nodejs, but in browser(chrome), it will still take some time because chrome does't open javascript version of AsyncHooks and PromiseHooks for now.

Solution 3:

It's got to do with how change detection works in Angular. See: https://stackblitz.com/edit/angular-jajbza?file=app%2Fapp.component.ts

I'm betting Ionic uses OnPush strategy by default, or you've enabled it, as I did in the Blitz. It's all good, IMO it should be on by default anyway as it forces you to think about these things and write more performant code.

Can't say exatly why your view is updated in your last example when you call .then though. Maybe in that case CD manages to keep track of the Promise, but not with async functions. Async functions themselves return a Promise if you don't, so basically after transpilation, async handle() returns something like Promise.resolve(null), though the actual JS code probably looks messier than that.

Edit: Another way, perhaps cleaner than calling detectChanges manually would be to run anything that changes the view inside Angular's zone:

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

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

Edit2: Interesting, zone.run() actually doesn't change anything. Tested that in the blitz. So manual CD is the only way. One more reason to avoid async/await and try to stick with Observables and NG's async pipe. :)

Solution 4:

Check your browser version and what it supports, async/await is native with ES2017. If your browser doesn't support that use ES2016 as your target.

I needed ES2016 for Electron.

Post a Comment for "Angular Data Binding Won't Work With Async/await, Yet It Will With Promises"