Sukima28th April 2022 at 2:17pm
Inspired from:
- Asynchronous Computed Properties in Ember by Sukima
- Async Data and Autotracking in Ember Octane by Chris Krycho
Helper
import { Helper } from '@ember/helper';
import { tracked } from '@glimmer/tracking';
const STATES = new WeakMap();
const TASKS = new WeakMap();
class State {
@tracked state = { status: 'running' };
get status() {
return this.state.status;
}
get value() {
return this.state.value;
}
get error() {
return this.state.reason;
}
resolveWith(value) {
this.state = { status: 'done', value };
}
rejectWith(reason) {
this.state = { status: 'error', reason };
}
static for(key) {
let state = STATES.get(key) ?? new State();
STATES.set(key, state);
return state;
}
}
class Task {
constructor(promise) {
const state = State.for(this);
this.promise = promise;
promise.then(
value => state.resolveWith(value),
error => state.rejectWith(error),
);
}
get status() {
return State.for(this).status;
}
get value() {
return State.for(this).value;
}
get error() {
return State.for(this).error;
}
get is() {
return { [this.status]: true };
}
then(...args) {
return Task.from(this.promise.then(...args));
}
catch(...args) {
return Task.from(this.promise.catch(...args));
}
finally(...args) {
return Task.from(this.promise.finally(...args));
}
static from(promise) {
let task = TASKS.get(promise) ?? new Task(promise);
TASKS.set(promise, task);
return task;
}
}
export function load(promise) {
return Task.from(promise);
}
export default Helper.helper(
([promise]) => load(promise);
);
Usage
{{#let (load this.myPromise) as |task|}}
{{#if task.is.running}}
<LoadingIndicator />
{{else if task.is.error}}
<Error @error={{task.error}} />
{{else}}
<DataTable @data={{task.value}} />
{{/if}}
{{/let}}