I was just trying different combinations of async await problem statements and I tried this,
basically I have two functions promise1() and promise2() which return two promises which resolve values 10 and 20 after 3 and 1 seconds respectively. Below is the code that I have written
function promise1()
{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(10)
},3000)
})
}
function promise2()
{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(20)
},1000)
})
}
async function sum()
{
let num1 = await promise1();
let num2 = await promise2();
return num1+num2;
}
sum().then(s => console.log(s))
when I execute this code I am getting 30 as answer
as in the above code if I was able to apply .then() to sum then is it a promise?
when I did console.log(typeof sum) its saying sum is a function not an object so what exactly are async functions? I have tried searching the answer for this behaviour but couldn't find any sources that would correctly answer. It would be a great help for me if someone answered this query or tell me the online sources or books that would get me the answer
my node version is v10.15.3
thank you
They are functions where the return value is always a promise.
async function foo() { }
const value = foo();
console.log(foo instanceof Function);
console.log(foo instanceof Promise);
console.log(value instanceof Function);
console.log(value instanceof Promise);
as in the above code if I was able to apply .then() to sum then is it a promise?
No, you weren't.
sum.then() won't work. sum().then() will. Adding () will call a function and give you the return value.
Are async functions really functions or Promise objects?
They're really functions. The functions return promises.
as in the above code if I was able to apply .then() to sum then is it a promise?
You didn't. You used .then() on the result of calling sum (sum().then(...), not sum.then(...)). It's just like calling a function that returns a string, then using toUpperCase on what the function returns.
These two functions are essentially equivalent, handwaving some minor details:
// #1
function foo() {
return new Promise((resolve, reject) => {
try {
getSomePromise()
.then(result => result * 2)
.catch(reject);
} catch (error) {
reject(error);
}
});
}
and
// #2
async function foo() {
const result = await getSomePromise();
return result * 2;
}
Naturally, that's not how you'd probably write #1 in real life, it's an example of the promise creation antipattern, but it's a reasonable interpretation of what an async function looks like under the hood. Purely for completeness, you'd probably write #1 like this:
// #3
function foo() {
return getSomePromise().then(result => result * 2);
}
The difference is that if getSomePromise throws an error rather than running and returning a promise, that function (#3) will throw an error (synchronously), whereas #1 above will return a promise that rejects instead. async functions do the latter, which is why #1 is written the way it is above.
Related
There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.
There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.
I am trying to understand some of the nuances of async/await in Javascript and was hoping I can get some help here.
Suppose I have the following functions:
async function sum(a, b) {
return await adder(a, b);
}
function sum(a, b) {
return adder(a, b);
}
It's my understanding that the return values of the above functions are the same, due to async returning a promise, or wrapping non-promises.
Suppose adder returns a number. The first function will first resolve the await adder which will be a number, but then the async will wrap it as a Promise<number>.
Similarly, for the second one, a regular Promise<number> will be returned.
Am I correct in saying this?
It's my understanding that the return values of the above functions are the same...
Not quite, no, but you seem to know that:
Suppose adder returns a number. The first function will first resolve the await adder which will be a number, but then the async will wrap it as a Promise<number>.
Similarly, for the second one, a regular Promise<number> will be returned.
No, not if adder returns a number. The second one (the non-async sum) will return a number, not a Promise<number>, because neither it nor adder is async.
If adder returns a native Promise<number>, then the return values of async function sum and function sum are the same, although there may be a slight difference in timing.
There's never any real reason to use return await x unless you have it wrapped in a try/catch and you want to handle any error from x locally in the function. Otherwise, just return is sufficient, even if what you're returning is a promise. Until the ES2019 spec, return await somePromise and return somePromise were handled slightly differently, the settlement of the first was delayed one async tick longer than the settlement of the second, but if it's a native promise, a change in the ES2019 spec makes them the same even at that level. (But if somePromise were a non-native thenable, the extra tick would remain.)
Please refer to the inline comment for details.
async function sum(a, b) {
return a + b;
}
const promise = sum(1, 2);
// Here calling sum will return promise.
// Whatever you return it will be wrraped in promise[for simplicity]
// Above code is similar to
function sum1(a, b) {
return Promise.resolve(a + b);
}
// Assuming adder funtion is async function
function adder(a, b) {
return new Promise((r) => {
setTimeout(r, 1000, a + b);
});
}
// So calling adder
async function sum2(a, b) {
return await adder(a, b);
}
// Equivalent to
async function sum3(a, b) {
const data = await adder(a, b);
return Promise.resolve(data);
}
// sum3 will return promise
sum3(1, 2).then(console.log);
// which is equivalent to
async function sum4(a, b) {
return adder(a, b);
}
// sum4 will return promise
sum3(1, 2).then(console.log);
async functions always return promises.
so assuming adder is async function both of the functions return promises.
in function 1 it will wait for the promise returned from adder to be resolved, once resolved it will return the number value, but async function always wraps the return value in promise if it isn't the in first place. so it returns a promise.
in function 2 it is because it returns the return value of adder which is a promise;
think of the return clause in async function as resolving the promise it returns.
There is quite some topics posted about how async/await behaves in javascript map function, but still, detail explanation in bellow two examples would be nice:
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
edited: this if of course a fictional case, so just opened for debate, why,how and when should map function wait for await keyword. solutions how to modify this example, calling Promise.all() is kind of not the aim of this question.
getResult is an async function
The other answers have pretty well covered the details of how your examples behave, but I wanted to try to state it more succinctly.
const resultsPromises = myArray.map(async number => {
return await getResult(number);
});
const resultsPromises = myArray.map(number => {
return getResult(number);
});
Array.prototype.map synchronously loops through an array and transforms each element to the return value of its callback.
Both examples return a Promise.
async functions always return a Promise.
getResult returns a Promise.
Therefore, if there are no errors you can think of them both in pseudocode as:
const resultsPromises = myArray.map(/* map each element to a Promise */);
As zero298 stated and alnitak demonstrated, this very quickly (synchronously) starts off each promise in order; however, since they're run in parallel each promise will resolve/reject as they see fit and will likely not settle (fulfill or reject) in order.
Either run the promises in parallel and collect the results with Promise.all or run them sequentially using a for * loop or Array.prototype.reduce.
Alternatively, you could use a third-party module for chainable asynchronous JavaScript methods I maintain to clean things up and--perhaps--make the code match your intuition of how an async map operation might work:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const getResult = async n => {
await delay(Math.random() * 1000);
console.log(n);
return n;
};
(async () => {
console.log('parallel:');
await AsyncAF([1, 2, 3]).map(getResult).then(console.log);
console.log('sequential:');
await AsyncAF([1, 2, 3]).series.map(getResult).then(console.log)
})();
<script src="https://unpkg.com/async-af#7.0.12/index.js"></script>
async/await is usefull when you want to flatten your code by removing the .then() callbacks or if you want to implicitly return a Promise:
const delay = n => new Promise(res => setTimeout(res, n));
async function test1() {
await delay(200);
// do something usefull here
console.log('hello 1');
}
async function test2() {
return 'hello 2'; // this returned value will be wrapped in a Promise
}
test1();
test2().then(console.log);
However, in your case, you are not using await to replace a .then(), nor are you using it to return an implicit Promise since your function already returns a Promise. So they are not necessary.
Parallel execution of all the Promises
If you want to run all Promises in parallel, I would suggest to simply return the result of getResult with map() and generate an array of Promises. The Promises will be started sequentially but will eventually run in parallel.
const resultsPromises = indicators.map(getResult);
Then you can await all promises and get the resolved results using Promise.all():
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
Promise.all(data.map(getResult)).then(console.log);
Sequential execution of the Promises
However, if you want to run each Promise sequentially and wait for the previous Promise to resolve before running the next one, then you can use reduce() and async/await like this:
const data = [1, 2, 3];
const getResult = x => new Promise(res => {
return setTimeout(() => {
console.log(x);
res(x);
}, Math.random() * 1000)
});
data.reduce(async (previous, x) => {
const result = await previous;
return [...result, await getResult(x)];
}, Promise.resolve([])).then(console.log);
Array.prototype.map() is a function that transforms Arrays. It maps one Array to another Array. The most important part of its function signature is the callback. The callback is called on each item in the Array and what that callback returns is what is put into the new Array returned by map.
It does not do anything special with what gets returned. It does not call .then() on the items, it does not await anything. It synchronously transforms data.
That means that if the callback returns a Promise (which all async functions do), all the promises will be "hot" and running in parallel.
In your example, if getResult() returns a Promise or is itself async, there isn't really a difference between your implementations. resultsPromises will be populated by Promises that may or may not be resolved yet.
If you want to wait for everything to finish before moving on, you need to use Promise.all().
Additionally, if you only want 1 getResults() to be running at a time, use a regular for loop and await within the loop.
If the intent of the first code snippet was to have a .map call that waits for all of the Promises to be resolved before returning (and to have those callbacks run sequentially) I'm afraid it doesn't work like that. The .map function doesn't know how to do that with async functions.
This can be demonstrated with the following code:
const array = [ 1, 2, 3, 4, 5 ];
function getResult(n)
{
console.log('starting ' + n);
return new Promise(resolve => {
setTimeout(() => {
console.log('finished ' + n);
resolve(n);
}, 1000 * (Math.random(5) + 1));
});
}
let promises = array.map(async (n) => {
return await getResult(n);
});
console.log('map finished');
Promise.all(promises).then(console.log);
Where you'll see that the .map call finishes immediately before any of the asynchronous operations are completed.
If getResult always returns a promise and never throws an error then both will behave the same.
Some promise returning functions can throw errors before the promise is returned, in this case wrapping the call to getResult in an async function will turn that thrown error into a rejected promise, which can be useful.
As has been stated in many comments, you never need return await - it is equivalent to adding .then(result=>result) on the end of a promise chain - it is (mostly) harmless but unessesary. Just use return.
I have to pass a function to another function, and execute it as a callback. The problem is that sometimes this function is async, like:
async function() {
// Some async actions
}
So I want to execute await callback() or callback() depending on the type of function that it is receiving.
Is there a way to know the type of the function??
Theory
Native async functions may be identifiable when being converted to strings:
asyncFn[Symbol.toStringTag] === 'AsyncFunction'
Or by AsyncFunction constructor:
const AsyncFunction = (async () => {}).constructor;
asyncFn instanceof AsyncFunction === true
This won't work with Babel/TypeScript output, because asyncFn is regular function in transpiled code, it is an instance of Function or GeneratorFunction, not AsyncFunction. To make sure that it won't give false positives for generator and regular functions in transpiled code:
const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;
(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
Since native async functions were officially introduced to Node.js in 2017, the question likely refers to Babel implementation of async function, which relies on transform-async-to-generator to transpile async to generator functions, may also use transform-regenerator to transpile generator to regular functions.
The result of async function call is a promise. According to the proposal, a promise or a non-promise may be passed to await, so await callback() is universal.
There are only few edge cases when this may be needed. For instance, native async functions use native promises internally and don't pick up global Promise if its implementation was changed:
let NativePromise = Promise;
Promise = CustomPromiseImplementation;
Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;
This may affect function behaviour (this is a known problem for Angular and Zone.js promise implementation). Even then it's preferable to detect that function return value is not expected Promise instance instead of detecting that a function is async, because the same problem is applicable to any function that uses alternative promise implementation, not just async (the solution to said Angular problem is to wrap async return value with Promise.resolve).
Practice
From the outside, async function is just a function that unconditionally returns native promise, therefore it should be treated like one. Even if a function once was defined async, it can be transpiled at some point and become regular function.
A function that can return a promise
In ES6, a function that potentially returns a promise can be used with Promise.resolve (lets synchronous errors) or wrapped Promise constructor (handles synchronous errors):
Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);
new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);
In ES2017, this is done with await (this is how the example from the question is supposed to be written):
let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...
A function that should return a promise
Checking if an object is a promise is a matter of a separate question, but generally it shouldn't be too strict or loose in order to cover corner cases. instanceof Promise may not work if global Promise was replaced, Promise !== (async () => {})().constructor. This can happen when Angular and non-Angular applications interface.
A function that requires to be async, i.e. to always return a promise should be called first, then returned value is checked to be a promise:
let promise = fnThatShouldReturnAPromise();
if (promise && typeof promise.then === 'function' && promise[Symbol.toStringTag] === 'Promise') {
// is compliant native promise implementation
} else {
throw new Error('async function expected');
}
TL;DR: async functions shouldn't be distinguished from regular functions that return promises. There is no reliable way and no practical reason to detect non-native transpiled async functions.
As long as only the native async functions are used (which is usually the case), I prefer this simple way:
theFunc.constructor.name == 'AsyncFunction'
Both #rnd, and #estus are correct.
But to answer the question with an actual working solution here you go
function isAsync (func) {
const string = func.toString().trim();
return !!(
// native
string.match(/^async /) ||
// babel (this may change, but hey...)
string.match(/return _ref[^\.]*\.apply/)
// insert your other dirty transpiler check
// there are other more complex situations that maybe require you to check the return line for a *promise*
);
}
This is a very valid question, and I'm upset that someone down voted him. The main usecase for this type of checking is for a library/framework/decorators.
These are early days, and we shouldn't downvote VALID questions.
In case you're using NodeJS 10.x or later
Use the native util function.
util.types.isAsyncFunction(function foo() {}); // Returns false
util.types.isAsyncFunction(async function foo() {}); // Returns true
Do keep all the concerns in mind from above ansers though. A function that just returns by accident a promise, will return a false negative.
And on top of that (from the docs):
Note that this only reports back what the JavaScript engine is seeing; in particular, the return value may not match the original source code if a transpilation tool was used.
But if you use async in NodeJS 10 and no transiplation. This is a nice solution.
It seems that await can be used for normal functions too. I'm not sure if it can be considered "good practice" but here it is:
async function asyncFn() {
// await for some async stuff
return 'hello from asyncFn'
}
function syncFn() {
return 'hello from syncFn'
}
async function run() {
console.log(await asyncFn()) // 'hello from asyncFn'
console.log(await syncFn()) // 'hello from syncFn'
}
run()
Here is a short and useful approach provided by David Walsh in his blogpost:
const isAsync = myFunction.constructor.name === "AsyncFunction";
Cheers!
TL;DR
Short answer: Use instaceof after exposing AsyncFunction - see below.
Long answer: Don't do that - see below.
How to do it
You can detect whether a function was declared with the async keyword
When you create a function, it shows that it's a type Function:
> f1 = function () {};
[Function: f1]
You can test it with the instanceof operator:
> f1 instanceof Function
true
When you create an async function, it shows that it's a type AsyncFunction:
> f2 = async function () {}
[AsyncFunction: f2]
so one might expect that it can be tested with instanceof as well:
> f2 instanceof AsyncFunction
ReferenceError: AsyncFunction is not defined
Why is that? Because the AsyncFunction is not a global object. See the docs:
https://developer.mozilla.org/Web/JavaScript/Reference/Global_Objects/AsyncFunction
even though, as you can see, it's listed under Reference/Global_Objects...
If you need easy access to the AsyncFunction then you can use my unexposed module:
https://www.npmjs.com/package/unexposed
to get either a local variable:
const { AsyncFunction } = require('unexposed');
or to add a global AsyncFunction alongside other global objects:
require('unexposed').addGlobals();
and now the above works as expected:
> f2 = async function () {}
[AsyncFunction: f2]
> f2 instanceof AsyncFunction
true
Why you shouldn't do it
The above code will test whether the function was created with the async keyword but keep in mind that what is really important is not how a function was created but whether or not a function returns a promise.
Everywhere where you can use this "async" function:
const f1 = async () => {
// ...
};
you could also use this:
const f2 = () => new Promise((resolve, reject) => {
});
even though it was not created with the async keyword and thus will not be matched with instanceof or with any other method posted in other answers.
Specifically, consider this:
const f1 = async (x) => {
// ...
};
const f2 = () => f1(123);
The f2 is just f1 with hardcoded argument and it doesn't make much sense to add async here, even though the result will be as much "async" as f1 in every respect.
Summary
So it is possible to check if a function was created with the async keyword, but use it with caution because you when you check it then most likely you're doing something wrong.
You can assume at begin that callback is promise:
export async function runSyncOrAsync(callback: Function) {
let promisOrValue = callback()
if (promisOrValue instanceof Promise) {
promisOrValue = Promise.resolve(promisOrValue)
}
return promisOrValue;
}
and them in your code you can do this:
await runSyncOrAsync(callback)
which will solve your problem with unknowing callback type....
Full Solution: Handle both Async and Promise
I always use Promises and async/await interchangeably, as they are basically the same.
Async/Await is used to work with promises in asynchronous functions. It is basically syntactic sugar for promises. It is just a wrapper to restyle code and make promises easier to read and use.
Source: GeeksForGeeks
If you need a helper function to determine if a value is an asynchronous function, without invoking it, or if a value is a function that returns a Promise, you have arrived at the right post.
In this example I will present three different approaches.
Check if function is an async/await function.
Check if a regular function returns a Promise.
Check both.
Handle Async Functions
This function can determine if a function was defined using the async keyword.
Example functions to validate
async function a() {}
const b = async () => {}
Validation function
function isAsyncFunction(f: unknown): boolean {
return f && f.constructor.name === 'AsyncFunction'
}
Handle Promise Functions
This function can determine if a regular function returns a Promise. In order assess if the given function returns a Promise, we need to invoke the function and examine the returned value. To avoid multiple invocations of the same function, we can return the the aforementioned value if it's a Promise, and false if it's not.
Example functions to validate
function a() { return new Promise(() => {}) }
const b = () => new Promise(() => {})
Validation function
function isPromiseFunction<T>(fn: any, ...params: any[]): Promise<T> | boolean {
const isFunction = fn && typeof fn === 'function'
const notAsyncFunction = fn.constructor.name !== 'AsyncFunction'
if (isFunction && notAsyncFunction) {
const value = fn(...params) || false
if (value && value.constructor.name === 'Promise') {
return value as Promise<T>
}
}
return false
}
Handle both
Because both AsyncFunction and Promise are essentially the same, we can just check if they both return a Promise.
function isAsync<T>(fn: any, ...params: any[]): Promise<T> | boolean {
const isFunction = fn && typeof fn === 'function'
if (isFunction) {
const value = fn(...params) || false
if (value && value.constructor.name === 'Promise') {
return value as Promise<T>
}
}
return false
}
Conclusion
Asynchronous functions are faster and cleaner to validate, whereas promise functions need to be invoked in order to be validated.
Test Functions (CodePen)
https://codepen.io/isakhauge/pen/gOGGQPY
So I want to execute await callback() or callback() depending on the
type of function that it is receiving.
You could always execute it with await and it will do the right thing:
async function main(callback) {
let result = await callback(); // even if callback is not async
// use 'result'
}
Is there a way to know the type of the function??
Maybe what you're actually interested in is the type of the result of the function. Dariusz Filipiak's answer is good but it can be even more concise:
async function main(callback) {
let result = callback();
if (result instanceof Promise) {
result = await result;
}
// use 'result'
}