npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ember-async-await-helper

v1.0.0

Published

The default blueprint for ember-cli addons.

Downloads

12,401

Readme

ember-async-await-helper

Build Status

Awaits a promise, then yields its result to a block. 👌

Compatibility

  • Ember.js v3.16 or above
  • Ember CLI v2.13 or above
  • Node.js v12 or above

Installation

ember install ember-async-await-helper

Usage

The {{#async-await}} template helper takes a promise as a positional parameter, a block to render once the promise is resolved. Once the promise is resolved, the helper yields the promise's result to the block.

{{#async-await this.users as |users|}}
    <UserList @users={{users}} />
{{/async-await}}

If the passed in value is not a promise, it will be converted to one using Promise.resolve().

Loading States

Optionally, you can pass an inverse block to be displayed while the promise is pending.

{{#async-await this.users as |users|}}
    <UserList @users={{users}} />
{{else}}
    <LoadingSpinner />
{{/async-await}}

Error Handling

In general, it's a bad idea to pass a fallible promise into the template. By default, if your promise rejects, {{#async-await}} calls Ember.onerror, which should trigger your typical error handling paths, such as showing a "something went wrong..." screen and/or reporting to Bugsnag.

The default error object comes with a reason property set to the promise's rejection reason:

Ember.onerror = function(error) {
  console.error(error.message); // => Unhandled promise rejection in {{#async-await}}: **rejection reason**

  console.error(error.reason); // => **rejection reason**
};

Note that after the promise rejects, the {{#async-await}} helper will remain in the "pending" state (i.e. the {{else}} block).

Recommended Method

In order to avoid dealing with rejections in the template, it is recommended that you wrap your promises in an async function that handles any expected error scenarios, so that the promise is (mostly) infallible:

export default Component.extend({
  users: computed(async function() {
    let retries = 0;

    while (retries < 5) {
      try {
        return await fetch('/users.json');
      } catch (e) {
        if (isNetworkError(e)) {
          retries += 1;
        } else {
          // Unexpected Error! We can let this trigger the default
          // `onReject` callback. In our `Ember.onerror` handler,
          // we will transition the app into a generic error route.
          throw e;
        }
      }
    }
  })
});

For any non-trivial functionality, you may also want to consider using an ember-concurrency task instead. Read on for how to use the {{#async-await}} helper together with ember-concurrency.

Inline onReject callbacks

While the above method is recommended, it is also possible to pass an onReject callback to run when the promise rejects:

{{#async-await this.users onReject=handleError as |users|}}
    <UserList @users={{users}} />
{{/async-await}}

As mentioned above, after the promise rejects, the {{#async-await}} helper will remain in the "pending" state (i.e. the {{else}} block). Your rejection handler can retry the original operation by replacing the promise passed to the {{#async-await}} helper:

export default Component.extend({
  // ...

  handleError(reason) {
    if (isNetworkError(reason)) {
      // retry the fetch
      this.set('users', fetch('/users.json'));
    } else {
      // show a "something went wrong" modal
      handleUnexpectedError(reason);
    }
  }
});

Finally, if you really want to, you can also pass null to silence the rejections completely:

{{#async-await this.users onReject=null as |users|}}
    <UserList @users={{users}} />
{{/async-await}}

Using with ember-concurrency

Did you know that ember-concurrency tasks (TaskInstances to be exact) are also promise-like objects (they have a .then method on them). That means, you can await them with the {{#async-await}} just like any other promises!

export default Component.extend({
  init() {
    this._super(...arguments);
    this.fetchUsers.perform();
  },

  users: alias('fetchUsers.last'),

  fetchUsers: task(function * () {
    let retries = 0;

    while (retries < 5) {
      try {
        return yield fetch('/users.json');
      } catch (e) {
        if (isNetworkError(e)) {
          retries += 1;
        } else {
          // this will trigger the default `onReject`
          throw e;
        }
      }
    }
  }).restartable()
});

With this setup, you can continue to pass this.users to the {{#async-await}} helper as you normally would:

{{#async-await this.users as |users|}}
    <UserList @users={{users}} />
{{else}}
    <LoadingSpinner />
{{/async-await}}

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.