This page is part of a static HTML representation of TriTarget.org at https://tritarget.org

Ember Async Getter

Sukima28th April 2022 at 2:17pm

Inspired from:

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}}