How to know if a function is async?

async-await, ecmascript-next, javascript, node.js

Solution

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.

Problem

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??

Original source

Related problems