MatrixAI / js-async-init

Asynchronous initialization and deinitialization decorators for JavaScript/TypeScript applications

Home Page:https://polykey.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The `ready` decorator should be capable of decorating regular functions returning promises, generators or async generators

CMCDragonkai opened this issue · comments

Specification

While developing the timed decorator, we discovered that the decorator may decorate a regular function that also returns a promise, generator or async generator. In such a case, the same behaviour should be applied as if it was an async function, generator function or async generator function.

This just needs to be done in the last block:

      descriptor[kind] = function (...args) {
        if (allowedStatuses.includes(this[_status])) {
          return f.apply(this, args);
        }
        if (this[initLock].isLocked('write') || this[_destroyed]) {
          resetStackTrace(errorDestroyed, descriptor[kind]);
          throw errorDestroyed;
        }
        return f.apply(this, args);
      };

Where after doing f.apply(this, args) we should be doing:

        const result = f.apply(this, params);
        if (utils.isPromise(result)) {
          // do as a promise
        } else if (utils.isIterable(result)) {
          // do as a generator
        } else if (utils.isAsyncIterable(result)) {
          // do as an async generator
        } else {
          // as usual
        }

We will need the conditional checks like isPromise, isIterable, isAsyncIterable.

Consider that you may only need isPromiseLike, and not isPromise because you only need then then function.

It may require some refactoring of the code to avoid too much duplication.

This will allow us to do:

class C {
  @ready()
  f(): Promise<void> {
    // ...
  }

  @ready()
  async g(): Promise<void> {
    // ...
  }
}

The C.f and C.g should behave the same with respect to readiness.

Additional context

Tasks

  1. ...
  2. ...
  3. ...

Note that this means the @ready decorator really works on almost anything. The @timed decorator however is limited to Function types.

It's actually kind of difficult to do this.

The only difference between async function and normal function when applying ready is the usage of block.

If it's an async function, the block changes the meaning. And it will first try to read lock the initLock before calling the function.

However for a normal function, the block is simply ignored. If we were to then check what the result is, it would be irrelevant because the function has already been called.

Therefore, checking the result is not useful here. This means if you have method that returns a promise like PromiseCancellable, using @ready on it will result in block having no effect.

So most of the time we are not using block. Therefore block will only work on properly async methods.

This means if we want to have a method return a promise cancellable, it would have to be an async method FIRST, then apply @ready to it, then wrap it as a PromiseCancellable.

That's the limitation here.

Beware of this issue @tegefaulkes. Don't use @ready with block=True on normal methods returning PromiseCancellable.

Closing this as wontfix.