Skip to content

Instantly share code, notes, and snippets.

@snewcomer
Last active October 22, 2021 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snewcomer/4ee2d7092742be8666343dbe0e56c1ee to your computer and use it in GitHub Desktop.
Save snewcomer/4ee2d7092742be8666343dbe0e56c1ee to your computer and use it in GitHub Desktop.
await non model hook promise ember
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { assert } from '@ember/debug';
import { tracked } from '@glimmer/tracking';
class AsyncData {
constructor(list) {
if (list) {
this._value = [];
}
}
/**
@type {'LOADING' | 'LOADED' | 'ERROR'}
@private
*/
@tracked _state = 'LOADING';
/** @private */
@tracked _value;
/** @private */
@tracked _error;
get state() {
return this._state;
}
get value() {
return this._value;
}
get error() {
assert(`You can only access 'error' when 'state' is 'ERROR', but it is ${this.state}`, this.state === 'ERROR');
return this._error;
}
get isLoading() {
return this.state === 'LOADING';
}
get isLoaded() {
return this.state === 'LOADED';
}
get isError() {
return this.state === 'ERROR';
}
resolveWith(value) {
this._state = 'LOADED';
// TODO: we need a macrotask here when *library gives us back a Promise that won't hit network.
// without, it will starve rendering and won't paint the header text (Browse) until all content is painted
this._value = value;
}
rejectWith(error) {
this._state = 'ERROR';
this._error = error;
}
}
class ResolvedData {
state = 'LOADED';
isLoading = false;
isLoaded = true;
isError = false;
constructor(data) {
this._value = data;
}
get value() {
return this._value;
}
}
const OUTSTANDING_PROMISES = new WeakMap();
export default class Await extends Helper {
@service router;
/**
* @method compute
* @public
* @param params Array a list of arguments passed to the Helper.
* @param hash Object a list of configuration options passed to the helper.
* @param hash.list We can render an empty list while the promise is outstanding, preventing errors downstream
*/
compute([maybePromise], { list = false }) {
const existingAsyncData = OUTSTANDING_PROMISES.get(maybePromise);
if (existingAsyncData) {
return existingAsyncData;
}
// this may be a resolved data structure from the model hook if we block there
if (!maybePromise || typeof maybePromise.then !== 'function') {
return new ResolvedData(maybePromise);
}
const asyncData = new AsyncData(list, this.router);
OUTSTANDING_PROMISES.set(maybePromise, asyncData);
maybePromise.then(
(value) => asyncData.resolveWith(value),
(error) => {
asyncData.rejectWith(error);
if (!this.router._router.outstandingTransitions.length) {
this.router.transitionTo('error');
}
}
);
return asyncData;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment