I am wondering if it is possible to dynamically create an async function like this:
new Function('await Promise.resolve()');
Expectedly, the previous code throw:
Uncaught SyntaxError: await is only valid in async function
Yes, you can get a reference to the non-global AsyncFunction constructor to dynamically create async functions.
You get a reference to the AsyncFunction constructor like this:
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
The signature of the AsyncFunction constructor is:
AsyncFunction(arg0?, arg1?, ...args?, functionBody: string);
NOTE: The AsyncFunction
contructor is not global (like Function
is), the only way to get a reference to it is via the prototype of an async function(){}
instance, as shown above and in the MDN docs.
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const myAsync = new AsyncFunction(`return true`);
const result = await myAsync(); // true
myAsync().then((result) => {
// result is true
});
The example above is equivalent to:
const myAsync = async () => {
return true;
};
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
// Define two named arguments: inValue, delay
const asyncFn = new AsyncFunction('inValue', 'delay', `return new Promise((resolve) => {
setTimeout(() => {resolve(inValue)}, delay);
});`);
// resolves to 'hello' after 100ms
const result = await asyncFn('hello', 100);
// After 200ms the promise will be resolved with the value 'world'
asyncFn('world', 200).then((result) => {
console.log(result);
});
The example above is equivalent to:
const asyncFn = async (inValue, delay) => {
return new Promise((resolve) => {
setTimeout(() => {resolve(inValue)}, delay);
});
};
Another cool thing is that the parameters are allowed to use defaults, destructuring, and rest parameters.
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
// Define defaults for inValue and delay
const asyncFn = new AsyncFunction('inValue = "Hello"', 'delay = 100', `return new Promise((resolve) => {
setTimeout(() => {resolve(inValue)}, delay);
});`);
// resolves to 'hello' after 100ms (because we defined default parameters)
const result = await asyncFn();
// resolves to 'world' after 200ms
const result = await asyncFn('world', 200);
Just note that it might be insecure to construct a function like this if the delay
value were to come from an untrusted source, like user input.
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
function createAsync(delay){
return new AsyncFunction('inValue', `return new Promise((resolve) => {
setTimeout(() => {resolve(inValue)}, ${delay});
});`);
}
const asyncFn = createAsync(100);
const asyncFn2 = createAsync(200);
// After 100ms this will resolve 'hello'
await result1 = asyncFn('hello');
// After 200ms this will resolve 'world'
await result2 = asyncFn2('world');
This is roughly equivalent to:
function createAsync(delay){
return async (inValue) => {
return new Promise((resolve) => {
setTimeout(() => {resolve(inValue)}, delay);
};
};
}
const asyncFn = createAsync(100);
// After 100ms this will resolve 'hello'
await result1 = asyncFn('hello');
// After 100ms this will resolve 'world'
await result2 = asyncFn('world');
And remember that you can use await
inside the dynamically created async function :)
Note that the same security considerations apply to the async function constructor as the Function
constructor and eval()
.
If you are constructing a new function with content received from an untrusted source then your script or application may be vulnerable to injection attacks.
Don't use new Function()
. I'd personally say NEVER use it unless you're a compiler because:
Creating functions dynamically in js simply requires you to declare a function expression:
function functionMaker() {
return function () {};
}
Therefore creating an async function dynamically is simply:
function asyncMaker() {
return async function () {};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With