Reputation: 48909
I'm trying to write a "mixing" for JavaScript classes (controllers, in my app) to automatically "await" for a given function to be resolved, before actually invoke the real methods. Real class methods should receive the resolved value as last argument.
Here is the code of useAwait
, where i'm looking for the static class property awaits
and wrapping the originalFunc
into a new async
one. I'm calling the new function passing the original arguments plus the asyncFn
call result:
const useAwait = (controller, asyncFn) => {
controller.constructor.awaits.forEach(func => {
const originalFunc = controller[func];
controller[func] = async (...args) => {
return originalFunc.apply(
controller,
[...args, await asyncFn.call(controller)]
);
};
});
}
So when useAwait(ctrl, this.load)
is invoked on an instance this class:
class Controller {
static awaits = ['foo', 'bar'];
promise;
constructor() {
useAwait(this, this.load);
}
async foo(e, resolved) {
return resolved;
}
bar(resolved) {
return resolved;
}
async load() {
if (!this.promise) {
this.promise = new Promise(resolve => setTimeout(() => {
resolve('Hello World!');
}, 3000));
}
return this.promise;
}
}
The problem: all seems fine for foo
(already async
), but it's not for bar
: the result is a Promise
because now the bar
is wrapped in async
(wan't before). I know that an async function result is wrapped into a Promise
. Codepen example where bar
call outputs "[object Promise]".
So the question is: in theory, I should check if the original function is async
and if it was not, await
for it's return value?
Upvotes: 2
Views: 605
Reputation: 1074585
...in theory, I should check if the original function is
async
and if it was not,await
for it's return value?"
It wouldn't matter, your wrapper is async
; an async
function always returns a promise, whether you use await
or not. Moreover, your wrapper can't be synchronous, because it needs to call awaitFn
(load
, in the example) and wait for its result.
If you're going to wrap originalFunction
(bar
) such that it waits for awaitFn
(load
) to complete, the wrapped version of it needs to be asynchronous (either async
, or return a promise explicitly [or accept a callback, but better IMHO to use promises]). It cannot be synchronous, because awaitFn
(load
) isn't synchronous.
If the class instance isn't ready for use when you construct it, you might consider using a static method to get an instance instead; the static instance would return a promise that it fulfills with the instance once load
is complete. Rough sketch:
class Controller {
dataFromLoadingProcess;
constructor(dataFromLoadingProcess) {
this.dataFromLoadingProcess = dataFromLoadingProcess;
}
async foo(e, resolved) {
// ...optionally use `this.dataFromLoadingProcess`...
return resolved;
}
bar(resolved) {
// ...optionally use `this.dataFromLoadingProcess`...
return resolved;
}
static async createInstance() {
await /*...the loading process...*/;
return new Controller(/*...data from loading process here, perhaps...*/)
}
}
Upvotes: 4