The most efficient and easy-to-understand solution is the simplest approach
type VoidToVoidFunc = () => void;
type VoidToPromiseVoidFunc = () => Promise<void>;
async function execute(func: VoidToVoidFunc | VoidToPromiseVoidFunc) {
await func();
}
execute(() => console.log("sync function", 1));
execute(async () => {
await delay(1000);
console.log("async function", 2);
});
function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Playground Link
async function execute(func) {
await func();
}
execute(() => console.log("sync function", 1));
execute(async () => {
await delay(1000);
console.log("async function", 2);
});
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
As demonstrated above, utilizing the await
keyword on a non-Promise value essentially wraps and unwraps it. Since your execute
function is marked as async
, it will always return a Promise
and execute asynchronously (scheduled for the next tick), making complex solutions unnecessary in this scenario.
Refer to the async function and Promise documentation on MDN for further information.
Hence, while there are alternative methods to determine if a callback may or may not return a Promise, such as
type VoidToVoidFunc = () => void;
type VoidToPromiseVoidFunc = () => Promise<void>;
async function execute(func: VoidToVoidFunc | VoidToPromiseVoidFunc) {
const possiblePromise = func();
if (possiblePromise && 'then' in possiblePromise) {
await possiblePromise;
}
}
execute(() => console.log("sync function", 1));
execute(async () => {
await delay(1000);
console.log("async function", 2);
});
function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Playground Link
async function execute(func) {
const possiblePromise = func();
if (possiblePromise && 'then' in possiblePromise) {
await possiblePromise;
}
}
execute(() => console.log("sync function", 1));
execute(async () => {
await delay(1000);
console.log("async function", 2);
});
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Simplicity is key in this situation.
Note: The void
type in TypeScript defines the function contract and guides type inference. However, every JavaScript and TypeScript function actually returns a value. In this case, the func
parameter in execute
can return either undefined
or a Promise
that resolves to undefined
.