GithubHelp home page GithubHelp logo

buschtoens / ember-concurrency-typescript Goto Github PK

View Code? Open in Web Editor NEW
8.0 4.0 0.0 1.52 MB

ember-concurrency + ember-cli-typescript + native classes = type safe 🥳

License: MIT License

JavaScript 75.39% HTML 14.33% TypeScript 10.28%
ember-addon emberjs ember-cli-addon ember-concurrency ember-cli-typescript typescript

ember-concurrency-typescript's Introduction

ember-concurrency-typescript

Build Status npm version Download Total Ember Observer Score
ember.js ember-cli ember-cli-babel
code style: prettier dependencies devDependencies

Use ember-concurrency fully type-checked with ember-cli-typescript & native class syntax.

Installation

ember install ember-concurrency-typescript

If you haven't already, you also need to install ember-concurrency and ember-cli-typescript:

ember install ember-concurrency ember-cli-typescript

Usage

import { task, taskGroup, timeout } from 'ember-concurrency';

class Foo {
  slowGreeting = task(function*(this: Foo, delay: number, name: string) {
    yield timeout(delay);
    return `Hello, ${name}!`;
  });

  someRestartableTask = task(function*(this: Foo) {
    // ...
  }).restartable();

  someTaskGroup = taskGroup()
    .enqueue()
    .maxConcurrency(1);

  someGroupTask = task(function*(this: Foo) {
    // ...
  }).group('someTaskGroup');

  anotherGroupTask = task(function*(this: Foo) {
    // ...
  }).group('someTaskGroup');

  async greetTomDale() {
    // Parameters and return type are inferred correctly!
    const greeting = await this.slowGreeting.perform(1000, 'Tom Dale');
    console.log(greeting);
  }
}

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.

ember-concurrency-typescript's People

Contributors

buschtoens avatar dependabot-preview[bot] avatar ember-tomster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

ember-concurrency-typescript's Issues

Task Modifiers

Unfortunately we cannot support task modifiers as easily as we planned initially in machty/ember-concurrency-decorators#50.

class Foo {
  doStuff = task(function*(this: Foo) {
    // ...
  }).restartable();

  perform() {
    this.doStuff.perform();
  }
}

Our type definition currently basically looks like this:

declare module 'ember-concurrency' {
  export function task<Args extends any[], R>(
    task: GeneratorFn<Args, R>
  ): Task<Args, Exclude<R, Promise<any>>>;

  export interface TaskProperty {
    restartable: () => TaskProperty;
    drop: () => TaskProperty;
    keepLatest: () => TaskProperty;
    enqueue: () => TaskProperty;
    maxConcurrency: (n: number) => TaskProperty;
    cancelOn: (eventName: string) => TaskProperty;
    group: (groupName: string) => TaskProperty;
    evented: () => TaskProperty;
    debug: () => TaskProperty;
    on: (eventName: string) => TaskProperty;
  }

  export interface Task<Args extends any[], T> {
    readonly isIdle: boolean;
    readonly isQueued: boolean;
    readonly isRunning: boolean;
    readonly last?: TaskInstance<T>;
    readonly lastCanceled?: TaskInstance<T>;
    readonly lastComplete?: TaskInstance<T>;
    readonly lastErrored?: TaskInstance<T>;
    readonly lastIncomplete?: TaskInstance<T>;
    readonly lastPerformed?: TaskInstance<T>;
    readonly lastRunning?: TaskInstance<T>;
    readonly lastSuccessful?: TaskInstance<T>;
    readonly performCount: number;
    readonly state: TaskState;
    perform(...args: Args): TaskInstance<T>;
    cancelAll(): void;
  }

  export interface TaskInstance<T> extends PromiseLike<T> {
    readonly error?: unknown;
    readonly hasStarted: boolean;
    readonly isCanceled: boolean;
    readonly isDropped: boolean;
    readonly isError: boolean;
    readonly isFinished: boolean;
    readonly isRunning: boolean;
    readonly isSuccessful: boolean;
    readonly state: TaskInstanceState;
    readonly value?: T;
    cancel(): void;
    catch(): RSVP.Promise<unknown>;
    finally(): RSVP.Promise<unknown>;
    then<TResult1 = T, TResult2 = never>(
      onfulfilled?:
        | ((value: T) => TResult1 | RSVP.Promise<TResult1>)
        | undefined
        | null,
      onrejected?:
        | ((reason: any) => TResult2 | PromiseLike<TResult2>)
        | undefined
        | null
    ): RSVP.Promise<TResult1 | TResult2>;
  }
}

The issue is that task returns a Task, so that can call perform etc. on the class property.

However, this means that you can't call .restartable() etc. (from TaskProperty) in the class property assignment.

I see the following solutions:

  • Change task to return a Task & TaskInstance, which loosens the type safety. 😦
  • Allow usage of modifiers as decorators, e.g.:
    @restartable
    someTask = task(function*() { ... });
  • Accept modifiers as an options object, ember-concurrency-decorators style:
    someTask = task({ restartable: true }, function*() { ... });
  • Allow chainable API, but require a "terminator":
    someTask = task(function*() { ... }).restartable().task;
    regularTask = task(function*() { ... }).task;
  • "Inline" chainable API:
    someTask = task(function*() { ... }, t => t.restartable());
    or
    someTask = task(function*() { ... }, task.restartable());

Fails if there are 'classic' classes present

If used in a mixed project (like one transitioning to typescript), fails hard when tasks are present in classic classes.

For example

  test('Classic class', async function(assert) {
    const subject = EmberObject.create({
      testTask: task(function*() {
        yield timeout(1);
      }),
      async perform() {
        await timeout(1);
        return 'done';
      }
    })

    const value = await subject.perform();
    assert.equal(value, 'done');
  });

results in

Build Error (broccoli-persistent-filter:Babel > [Babel: ember-concurrency-typescript]) in dummy/tests/unit/task-test.ts

/addons/ember-concurrency-typescript/dummy/tests/unit/task-test.ts: `task` has to be a direct child of a `ClassProperty`.
  30 |   test('Classic class', async function(assert) {
  31 |     const subject = EmberObject.create({
> 32 |       testTask: task(function*() {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.