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
- MatrixAI/Polykey#297 (comment) - first applied here to
timed
decorator
Tasks
- ...
- ...
- ...
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.