Syed Huzaifa Hassan
Syed Huzaifa Hassan

Reputation: 796

ES 2017: async function vs AsyncFunction(object) vs async function expression

I was just reading about async functions and came across some similar features of ES2017. It has created a lot of confusion, I wanted to just ask:

  1. What is the difference between async function, AsyncFunction (which is used to create an async function), and an async fuction expression (which is what I think just another async function)?
  2. When should I use one format over another?

Highlights on the quirks and performance of each would be appreciated!

Upvotes: 7

Views: 2673

Answers (1)

lonesomeday
lonesomeday

Reputation: 237865

There are four ways to create a function in Javascript. There are also four ways to create an asynchronous function in Javascript, and they are precise mirrors of each other.

To demonstrate how this works, I'm using a simple sleep function, declared globally:

function sleep(time) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve();
        }, time);
    });
}

Function declarations

function speak() { return 'Hi'; }
async function speak() { await sleep(1000); return 'Hi'; }

This is the simplest way to declare a function. It can be declared once and is hoisted to the top of the current function scope.

Function declarations and asynchronous function declarations are exactly identical, except that the async function always returns a promise and allows you to use await.

Function expressions

let speak = function() { return 'Hi'; } // anonymous function expression
let speak = function speakFn() { return 'Hi'; } // named function expression

let speak = async function() { await sleep(1000); return 'Hi'; } // anonymous asynchronous function expression
let speak = async function speakFn() { await sleep(1000); return 'Hi'; } // named asynchronous function expression

Function expressions look very much like function declarations. They are not, however, hoisted to the top of the function scope. They can be redefined as many times as you like. They can be defined inline. They can either be anonymous or named: if they are named, then the name refers to the function within the scope of that function.

Function expressions and asynchronous function expressions are exactly identical, except that the async function always returns a promise and allows you to use await.

Arrow functions

let speak = word => 'Hi ' + word; // one parameter
let speak = (word1, word2) => 'Hi ' + word1 + word2; // multiple parameters

let speak = async word => { await sleep(1000); return 'Hi ' + word; } // one parameter
let speak = async (word1, word2) => { await sleep(1000); return 'Hi ' + word1 + word2; } // multiple parameters

Arrow functions are a quick and short way to define a function, introduced in ES2015 (ES6). They are in most ways equivalent to function expressions, except that they are always anonymous and the value of this is always lexically bound, i.e. inherited from the outer scope.

Arrow functions and asynchronous arrow functions are exactly identical, except that the async function always returns a promise and allows you to use await. (They are slightly different in the statements above because the asynchronous functions each have more than one statement within them. This means that the statements need to be enclosed in a block {} and the return needs to be explicit. This is also true of normal arrow functions that are more than one statement long.)

Function constructors

let speak = new Function('word', 'return "Hi " + word;');
let speak = new AsyncFunction('word', 'await sleep(1000); return "Hi " + word;')

Function constructors allow you to define a function dynamically by using strings. Note that they are always run in the global scope and have no access to the scope in which they were defined. They are only useful in a very small set of circumstances. I personally can't see how an asynchronous function constructor would ever be a useful thing. The writers of ES2017 agree with me, as AsyncFunction is not a global object and must be obtained first with const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor.

Functions created with the function constructor and functions created with the anonymous function constructor are exactly identical, except that the async function always returns a promise and allows you to use await. (But you'd already guessed that, right?)

Upvotes: 13

Related Questions