How to know if a function is async? - javascript

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'
}

Related

How to properly use async/await within function? [duplicate]

Given the code samples below, is there any difference in behavior, and, if so, what are those differences?
return await promise
async function delay1Second() {
return (await delay(1000));
}
return promise
async function delay1Second() {
return delay(1000);
}
As I understand it, the first would have error-handling within the async function, and errors would bubble out of the async function's Promise. However, the second would require one less tick. Is this correct?
This snippet is just a common function to return a Promise for reference.
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
Most of the time, there is no observable difference between return and return await. Both versions of delay1Second have the exact same observable behavior (but depending on the implementation, the return await version might use slightly more memory because an intermediate Promise object might be created).
However, as #PitaJ pointed out, there is one case where there is a difference: if the return or return await is nested in a try-catch block. Consider this example
async function rejectionWithReturnAwait () {
try {
return await Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
async function rejectionWithReturn () {
try {
return Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
In the first version, the async function awaits the rejected promise before returning its result, which causes the rejection to be turned into an exception and the catch clause to be reached; the function will thus return a promise resolving to the string "Saved!".
The second version of the function, however, does return the rejected promise directly without awaiting it within the async function, which means that the catch case is not called and the caller gets the rejection instead.
As other answers mentioned, there is likely a slight performance benefit when letting the promise bubble up by returning it directly — simply because you don’t have to await the result first and then wrap it with another promise again. However, no one has talked about tail call optimization yet.
Tail call optimization, or “proper tail calls”, is a technique that the interpreter uses to optimize the call stack. Currently, not many runtimes support it yet — even though it’s technically part of the ES6 Standard — but it’s possible support might be added in the future, so you can prepare for that by writing good code in the present.
In a nutshell, TCO (or PTC) optimizes the call stack by not opening a new frame for a function that is directly returned by another function. Instead, it reuses the same frame.
async function delay1Second() {
return delay(1000);
}
Since delay() is directly returned by delay1Second(), runtimes supporting PTC will first open a frame for delay1Second() (the outer function), but then instead of opening another frame for delay() (the inner function), it will just reuse the same frame that was opened for the outer function. This optimizes the stack because it can prevent a stack overflow (hehe) with very large recursive functions, e.g., fibonacci(5e+25). Essentially it becomes a loop, which is much faster.
PTC is only enabled when the inner function is directly returned. It’s not used when the result of the function is altered before it is returned, for example, if you had return (delay(1000) || null), or return await delay(1000).
But like I said, most runtimes and browsers don’t support PTC yet, so it probably doesn’t make a huge difference now, but it couldn’t hurt to future-proof your code.
Read more in this question: Node.js: Are there optimizations for tail calls in async functions?
Noticeable difference: Promise rejection gets handled at different places
return somePromise will pass somePromise to the call site, and await somePromise to settle at call site (if there is any). Therefore, if somePromise is rejected, it will not be handled by the local catch block, but the call site's catch block.
async function foo () {
try {
return Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'OUT'
return await somePromise will first await somePromise to settle locally. Therefore, the value or Exception will first be handled locally. => Local catch block will be executed if somePromise is rejected.
async function foo () {
try {
return await Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'IN'
Reason: return await Promise awaits both locally and outside, return Promise awaits only outside
Detailed Steps:
return Promise
async function delay1Second() {
return delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Async functions will wrap their return value inside Promise.resolve()(Source). Because delay1Second is an async function, we have:
const result = await Promise.resolve(delayPromise);
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
Promise.resolve(delayPromise) returns delayPromise without doing anything because the input is already a promise (see MDN Promise.resolve):
const result = await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
await waits until the delayPromise is settled.
IF delayPromise is fulfilled with PromiseValue=1:
const result = 1;
ELSE is delayPromise is rejected:
// jump to catch block if there is any
return await Promise
async function delay1Second() {
return await delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Local await will wait until delayPromise gets settled.
Case 1: delayPromise is fulfilled with PromiseValue=1:
async function delay1Second() {
return 1;
}
const result = await Promise.resolve(1); // let's call it "newPromise"
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: 1
const result = 1;
Case 2: delayPromise is rejected:
// jump to catch block inside `delay1Second` if there is any
// let's say a value -1 is returned in the end
const result = await Promise.resolve(-1); // call it newPromise
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: -1
const result = -1;
Glossary:
Settle: Promise.[[PromiseStatus]] changes from pending to resolved or rejected
This is a hard question to answer, because it depends in practice on how your transpiler (probably babel) actually renders async/await. The things that are clear regardless:
Both implementations should behave the same, though the first implementation may have one less Promise in the chain.
Especially if you drop the unnecessary await, the second version would not require any extra code from the transpiler, while the first one does.
So from a code performance and debugging perspective, the second version is preferable, though only very slightly so, while the first version has a slight legibility benefit, in that it clearly indicates that it returns a promise.
In our project, we decided to always use 'return await'.
The argument is that "the risk of forgetting to add the 'await' when later on a try-catch block is put around the return expression justifies having the redundant 'await' now."
Here is a typescript example that you can run and convince yourself that you need that "return await"
async function test() {
try {
return await throwErr(); // this is correct
// return throwErr(); // this will prevent inner catch to ever to be reached
}
catch (err) {
console.log("inner catch is reached")
return
}
}
const throwErr = async () => {
throw("Fake error")
}
void test().then(() => {
console.log("done")
}).catch(e => {
console.log("outer catch is reached")
});
here i leave some code practical for you can undertand it the diferrence
let x = async function () {
return new Promise((res, rej) => {
setTimeout(async function () {
console.log("finished 1");
return await new Promise((resolve, reject) => { // delete the return and you will see the difference
setTimeout(function () {
resolve("woo2");
console.log("finished 2");
}, 5000);
});
res("woo1");
}, 3000);
});
};
(async function () {
var counter = 0;
const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
if (counter == 7) {
clearInterval(a);
}
console.log(counter);
counter = counter + 1;
}, 1000);
console.time("time1");
console.log("hello i starting first of all");
await x();
console.log("more code...");
console.timeEnd("time1");
})();
the function "x" just is a function async than it have other fucn
if will delete the return it print "more code..."
the variable x is just an asynchronous function that in turn has another asynchronous function, in the main of the code we invoke a wait to call the function of the variable x, when it completes it follows the sequence of the code, that would be normal for "async / await ", but inside the x function there is another asynchronous function, and this returns a promise or returns a" promise "it will stay inside the x function, forgetting the main code, that is, it will not print the" console.log ("more code .. "), on the other hand if we put" await "it will wait for every function that completes and finally follows the normal sequence of the main code.
below the "console.log (" finished 1 "delete the" return ", you will see the behavior.

Javascript return same value in then and catch

In JS, let's say I call a promise in a 'fire and forget' way. I don't care if it was successful or not, and I don't care what it returns as I want to return the same value anyways.
I can call
return myPromise().then(() => foo).catch(() => foo);
Is there a way to not repeat then then and catch parts?
What I've tried
I thought about using finally, thinking that it will take what finally returns (similar to what Java does by taking the return value of the finally block) :
return myPromise().finally(() => foo);
But it turns out it didn't do what I expected. According to mdn, this return value will be used to reject in case the original promise rejected.
You can declare your result returning function once and then re-use it twice for the .then() and .catch() handlers
const myReturn = () => foo;
return myPromise()
.then(myReturn)
.catch(myReturn);
You can further shorten this by using both parameters of .then() which handle both the success and failure path:
const myReturn = () => foo;
return myPromise()
.then(myReturn, myReturn);
If you find needing this a lot, you might make a small library function that takes a value and returns two functions that both just evaluate to it:
const doubleThunk = val =>
[() => val, () => val];
Which would be used like this:
return myPromise()
.then(...doubleThunk(foo));
From #deceze's comment and #Bergi clarification:
myPromise()
.catch(() => {}) // ignore any error
.then(() => foo) // always return foo
You could have a look at Promise.allSettled if you're targeting newer browsers or Node.js versions.
This function will return a Promise that resolves whether or not the passed Promise resolves or rejects.
function foo(results) {
console.log('foo():', ...results)
}
// Success path
Promise.allSettled([Promise.resolve('Yay')]).then(foo)
// Failure path
Promise.allSettled([Promise.reject('Doh')]).then(foo)
.as-console-wrapper { max-height: 100% !important; }

Return result of .then() lambda expression as function result

I'm relatively new to js so please forgive me if my wording isn't quite right. I've also created a jsfiddle to demonstrate the issue.
Overview
In the app I'm working on, I have a function with a jquery ajax call, like this:
function scenario1(ajaxCfg) {
return $.ajax(ajaxCfg)
}
I want to change this function, but without in any way changing the inputs or outputs (as this function is called hundreds of times in my application).
The change is to make a different ajax call, THEN make the call specified. I currently have it written like this:
function callDependency() { //example dependency
return $.ajax(depUri)
}
function scenario2(ajaxCfg) {
return callDependency().then(() => $.ajax(ajaxCfg))
}
Desired Result
I want these two returned objects to be identical:
let result1 = scenario1(exampleCall)
let result2 = scenario2(exampleCall)
More specifically, I want result2 to return the same type of object as result1.
Actual Result
result1 is (obviously) the result of the ajax call, which is a jqXHR object that implements the promise interface and resolves to the same value as result2, which is a standard promise.
Since result2 is not a jqXHR object, result2.error() is undefined, while result1.error() is defined.
I did attempt to mock up these methods (simply adding a .error function to the return result, for example), but unfortunately even when doing this, result1.done().error is defined while result2.done().error is undefined.
Wrapping (or unwrapping) it up
In a nutshell, I want to return the jqXHR result of the .then() lambda function in scenario2 as the result of the scenario2 function. In pseudocode, I want:
function scenario2(ajaxCfg) {
return callDependency().then(() => $.ajax(ajaxCfg)).unwrapThen()
} //return jqXHR
What about something like this? The approach is a little different, but in the end you can chain .done() etc. to the scenario2() function:
const exampleCall = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
const depUri = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
function callDependency() { //example dependency
return $.ajax(depUri).done(() => console.log('returned callDependancy'))
}
let obj = { //creating an object with the scenario2 as a method so that I can bind it with defer.promise()
scenario2: function(ajaxCfg) {
return $.ajax(ajaxCfg).done(() => console.log('returned senario2')) // Purposely NOT calling the exampleCall() function yet
}
}
defer = $.Deferred(); // Using some JQuery magic to be able to return a jqXHR
defer.promise(obj); // Set the object as a promise
defer.resolve(callDependency()); // Invoking the callDependency() by default on promise resolve
obj.done(() => {
obj.scenario2() // Resolving so the callDependency() function can be called
}).scenario2(exampleCall).done(() => { // Here you can invoke scenario2 and FINALLY chain whatever you want after everything has been called
console.log('Here I can chain whatever I want with .done\(\) or .fail\(\) etc.')
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
What I think is cool about this way of doing it is that you can just keep adding methods to the object that you created, and then all your secondary functions that are built on top of callDependency() can be in one place. Not only that, but you can reuse those same methods on top of other AJAX calls.
Read more about this here.
I hope this helps!
I feel like your life would be made a lot easier if you used async/await syntax. Just remember though that async functions return a promise. So you could instead write:
async function scenario2(ajaxCfg) {
let jqXhrResult;
try {
await callDependency();
jqXhrResult = {
jqXhr: $.ajax(ajaxCfg)
};
} catch() {
// Error handling goes here
}
return jqXhrResult;
}
I actually thought of a way easier way to do this.
You can do it by adding a method to the function constructor's prototype object. That way any created function can inherit that method and you can still use the .done() syntax. It's referred to as prototypal inheritance:
const exampleCall = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
const depUri = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
function callDependency() {
return $.ajax(depUri).done(() => console.log('returned callDependancy'))
}
Function.prototype.scenario2 = function(ajaxCfg, ...args) {
return this(...args).then(() => $.ajax(ajaxCfg))
}
callDependency.scenario2(exampleCall).done(data => {
console.log(data)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Are async functions really functions or Promise objects?

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.

ES8 Immediately invoked async function expression

I haven't seen these constructs used much but I've found myself writing them to make use of async / await in functions that wouldn't typically return a promise, for example
chan.consume(queue, (msg) => {
this.pendingMsgs++; // executed immediately
(async () => {
await this.handleMessage(msg);
this.pendingMsgs--;
if (cancelled && this.pendingMsgs === 0) {
await chan.close();
await this.amqpConnectionPool.release(conn);
}
})();
});
as opposed to
chan.consume(queue, async (msg) => { // external lib does not expect a return value from this callback
this.pendingMsgs++; // executed in promise context(?)
await this.handleMessage(msg);
this.pendingMsgs--;
if (cancelled && this.pendingMsgs === 0) {
await chan.close();
await this.amqpConnectionPool.release(conn);
}
});
or
chan.consume(queue, (msg) => {
this.pendingMsgs++; // no await - excess function decls & nesting
this.handleMessage(msg).then(() => {
this.pendingMsgs--;
if (cancelled && this.pendingMsgs === 0) {
chan.close().then(() => {
this.amqpConnectionPool.release(conn);
});
}
});
});
Is this 'a thing'? Are there pitfalls here I should be aware of?
What's the lowdown on use of async / await in these kind of situations?
Is this 'a thing'?
Yes. It comes up every now and then, e.g. here. They're known as IIAFEs :-)
If you want to put focus on the arrow, you could also call them IIAAFs.
Are there pitfalls here I should be aware of?
Whenever you call a promise-returning function and don't return the result to somewhere else, you are responsible for the promise yourself - which means that you have to handle errors from it. So the pattern should in general look like
(async () => {
…
})().catch(err => {
console.error(err);
});
if you don't want to concern yourself with unhandled-rejection events.
What's the lowdown on use of async/await in these kind of situations?
Not much, compared to the then version. However, you say "the external lib does not expect a return value from this callback", which might hint at the library's incompatibility with asynchronous callbacks, so beware what you are doing when. It also might depend on exceptions being thrown synchronously from the callback, so it all depends on what the library expects here (and if there are no expectations, whether that may change in the future). You don't want future incompatibilities in case the library will start to treat promise return values specially.
However, I would still recommend the second pattern that directly passes the async function directly as the callback because of its better readability. If you want to avoid returning a promise to the library, create a helper function that wraps the callback:
function toVoid(fn) {
return (...args) => void fn(...args);
}
function promiseToVoid(fn) {
return (...args) => void fn(...args).catch(console.error);
}
which you could use like this:
chan.consume(queue, toVoid(async (msg) => {
… // use `await` freely
}));
(async () => {
await func();
})();

Categories

Resources