ES 2017: async function vs AsyncFunction(object) vs async function expression
Asked Answered
C

1

7

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!

Connection answered 16/2, 2017 at 1:26 Comment(4)
the same as function vs Function(object) vs function expression - except the function is asyncAlecalecia
there is nothing different?? (of course other than Asynchronous behavior)Connection
I didn't say there was nothing different ... I said that the differences are the same between the async flavour vs the "standard" flavour ... so, 1,2,3 is not the same as 4,5,6, but the differences between 1 and 2 and 3 is the same as 4 and 5 and 6 :pAlecalecia
yeah I get that, I meant difference is same as in the function, Function(Object) and function expression? I am still curious if something else is different.Connection
I
13

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

Inheritor answered 16/2, 2017 at 11:11 Comment(4)
I'd drop the statement "Function expressions are in most ways the same as function declarations.", but otherwise +1Jhelum
@Jhelum The syntax inside them is the same; they are called in the same way; they can be passed in the same way... They are much more similar than the equivalent syntax in PHP is, for instance.Inheritor
The syntax looks alike, yes, but no you cannot call or pass around a function declaration directly, you have to use the name. In contrast, you cannot use the name of a function expression, you need to do something with the result of the expression.Jhelum
@Jhelum For the sake of absolute clarity, I've edited my answer slightly. Thanks for your comments.Inheritor

© 2022 - 2024 — McMap. All rights reserved.