machty / ember-concurrency

ember-concurrency is an Ember Addon that enables you to write concise, worry-free, cancelable, restartable, asynchronous tasks.
http://ember-concurrency.com
MIT License
691 stars 155 forks source link

RFC: re-envision Encapsulated Tasks #466

Open machty opened 2 years ago

machty commented 2 years ago

Placeholder RFC; TODO: add more details.

This is a design for a future API for encapsulated tasks that should be TS-friendly and support all of today's use cases for encapsulated tasks:

class MyTaskInstance extends TaskInstance<MyComponent> {
  @tracked counter = 0;

  async perform(inc: number) {
    while(true) {
      await timeout(500);
      this.counter += inc;
      this.context.componentCounter += inc;
    }
  }
}

class MyComponent extends Component {
  @tracked componentCounter = 0;

  myTask = task({ drop:true }, MyTaskInstance);
}

// elsewhere: {{perform myTask 1}}, or this.myTask.perform(1)
machty commented 1 year ago

Playing around with the syntax a bit; the above could also be written as

class MyComponent extends Component {
  @tracked componentCounter = 0;

  static MyTaskInstance = class extends TaskInstance<MyComponent> {
    @tracked counter = 0;

    async perform(inc: number) {
      while(true) {
        await timeout(500);
        this.counter += inc;
        this.context.componentCounter += inc;
      }
    }
  }

  myTask = task({ drop:true }, MyComponent.MyTaskInstance);
}

or even

class MyComponent extends Component {
  @tracked componentCounter = 0;

  myTask = task(
    { drop: true },
    class extends TaskInstance<MyComponent> {
      @tracked counter = 0;

      async perform(inc: number) {
        while (true) {
          await timeout(500);
          this.counter += inc;
          this.context.componentCounter += inc;
        }
      }
    }
  );
}
maxfierke commented 1 year ago

I like the proposed API a lot because it removes the need to do a bunch of the meta-programming-type stuff we do now to mixin bits of the TaskInstance API with the user's object prototype. A couple of thoughts:

  1. We'll need to be a lot more thoughtful about API surface if we allow direct sub-classing of TaskInstance. We may want to refactor some things to discourage reaching into the juicy bits. Maybe time for private fields?

  2. this.context has always felt a little icky to me. I wonder if something a bit more guarded like a getContext or getHost yieldable (or I guess, Awaitable now?) would be a better method for getting a reference to the host object. It would discourage grabbing things off of this. that aren't defined locally in the subclass and also doesn't tie the task definition to an implementation detail of TaskInstance.