2

I'd like to convert this Ember route action to use ES2017 async / await. Can someone please explain what this would look like?

Per spec, I already added: babel: { includePolyfill: true } to my ember-cli-build.js file:

save() {
      let tenant = this.modelFor(this.routeName).tenant;
      let app = this.modelFor(this.routeName).app;

      return tenant.save().then(()=> {
        return tenant.get('app').save({ adapterOptions: { tenantId: tenant.id }}).then(() => {
          this.transitionTo('tenants.tenant.info', tenant.id);
        }).catch((error) => {
          tenant.get('app').rollback();
          throw error;
        });
      }).catch((error) => {
        tenant.rollback();
        throw error;
      });
    }
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
dpigera
  • 3,339
  • 5
  • 39
  • 60
  • UPDATE: Just learned that as of 6/2/2018, Ember does not support async/await in code. Only in tests. ember-concurrency is the next best option. – dpigera Jun 02 '18 at 15:12
  • 1
    thats not correct. you *can* use async/await. Just testing is a problem in some circumstances. What exactly is not working? – Lux Jun 03 '18 at 03:07
  • This is correct @Lux , you should avoid using async/await in app code for the time being, unless you are shipping out with the babel polyfill the native promise implementation is used and it is not run-loop aware, the conversation is best captured in the following [rfc](https://github.com/emberjs/rfcs/issues/175). Tl;dr use `ember-concurrency` or `ember-co` for now. – Patsy Issa Jun 04 '18 at 08:44
  • The autorun assertion what the comments above were about has been dropped: https://github.com/emberjs/ember.js/pull/16797 – jelhan Jul 24 '18 at 23:26

2 Answers2

1

Your code, converted to async/await:

async save() {
    let tenant = this.modelFor(this.routeName).tenant;
    let app = this.modelFor(this.routeName).app;

    try {
        await tenant.save();

        try {
            await tenant.get('app').save({ adapterOptions: { tenantId: tenant.id }});
            this.transitionTo('tenants.tenant.info', tenant.id);
        } catch (error) {
            tenant.get('app').rollback();
            throw error;
        }
    } catch (error) {
        tenant.rollback();
        throw error;
    }
}

To convert from promises, you add the await keyword to method calls that return promises. Everything that you place in the then method of the promise you can simply put after the await statement.

The promises' catch methods convert to regular try/catch blocks.

Patrick Hund
  • 19,163
  • 11
  • 66
  • 95
1

Very similar to the answer Patrick Hund wrote, but attaching the catch statements to the promises being awaited, rather than wrapping in try/catch blocks, and extracting the error logic into a single function.

async save() {
    let tenant = this.modelFor(this.routeName).tenant;
    let app = this.modelFor(this.routeName).app;

    await tenant.save().catch(handleError.bind(this, false));
    await tenant.get('app').save({ adapterOptions: { tenantId: tenant.id }})
                .catch(handleError.bind(this, true));

    this.transitionTo('tenants.tenant.info', tenant.id);

    // isApp is bound via bind; error will be provided when called via catch        
    function handleError(isApp, error) {
        if (isApp) {
            tenant.get('app').rollback();
        } else {
            tenant.rollback();
        }
        throw error;
    }
}
LocalPCGuy
  • 6,006
  • 2
  • 32
  • 28