Udo G
Udo G

Reputation: 13121

technical difference between ES7 async function and a promise?

I'm trying to understand better what an async function in JavaScript is technically, even if I basically know how to use them.

Many introductions to async/await make belive that an async function is basically just a promise, but that obviously is not the case (at least not with Babel6-transpiled code):

async function asyncFunc() {
  // nop
}

var fooPromise = new Promise(r => setTimeout(r, 1));

console.clear();

console.log("typeof asyncFunc is", typeof asyncFunc); // function
console.log("typeof asyncFunc.next is", typeof asyncFunc.next); // undefined
console.log("typeof asyncFunc.then is", typeof asyncFunc.then); // undefined

console.log("typeof fooPromise is", typeof fooPromise); // object
console.log("typeof fooPromise.next is", typeof fooPromise.next); // undefined
console.log("typeof fooPromise.then is", typeof fooPromise.then); // function

Still, it is definitely possible to await a promise, like await fooPromise().

Upvotes: 7

Views: 1612

Answers (2)

Chris Cinelli
Chris Cinelli

Reputation: 4829

The couple async/await are a mechanism that let you write async code in an synchronous style and in my humble opinion it is the most simple and readable syntax so far to deal with asynchronous code (see also this article). The power of the syntax is really in how the await works. But in order to use await inside the body of a function, the function must have to be prefixed with async.

If you need more information there is a spec for async/await here.

The current implementation in Babel 5 is based on https://github.com/facebook/regenerator. As you can see in the transpiled code the function is compiled to:

function asyncFunc(which, one, two) {
  return regeneratorRuntime.async(function asyncFuncMaybe$(context$1$0) {
...

If you dig in Babel's babel-regenerator-runtime package you find Facebook's code. At line 205 you find:

// Note that simple async functions are implemented on top of
// AsyncIterator objects; they just return a Promise for the value of
// the final result produced by the iterator.
runtime.async = function(innerFn, outerFn, self, tryLocsList) {
...

In order to transpile to ES5 the async/await Babel needs do to rearrange the code so we can keep track where we are during the execution of the function and the AsyncIterator is the object that keep track of that state.

Babel 6 gives you more options and let you choose the implementation that you want to use. See Transpile Async Await proposal with Babel.js?

So regarding your questions:

  • async/await are both a thing of it's own. According to the spec, they must work with promises. In particular you can await on a promise and when you execute an async function, it will return you a promise.
  • Since an async function is transpiled to function that return a promise, there is not a straightforward way to distinguish it from a no-async function that return a promise. Your fooPromise should look more like var fooPromiseFunc = function() {return new Promise(r => setTimeout(r, 1))}; that make fooPromiseFunc and asyncFunc indistinguishable from a black box prospective. They are both functions that return a promise. What is the reason why you want to distinguish an async and no-async function at runtime? In practice they can be usedin the same way, so I do not see why you will have to threat them differently. For debug purposes if you really need to find out if a function is defined async, in Babel 5 you could use something like (asyncFunc+"").indexOf('regeneratorRuntime.async') > 0 or a more accurate regular expression. But that is really hacky and I would not use in a context outside of debugging or study.

Upvotes: 0

Madara's Ghost
Madara's Ghost

Reputation: 174987

An async function is a function that returns a promise. It helps you with cases where you have a bunch of asynchronous actions happening one after the other:

function asyncFunc() {
  return doSomethingAsync() // doSomethingAsync() returns a promise
    .then(() => {
      // do some stuff
      return doSomethingElseAsync(); // returns a promise
    })
    .then(something => {
      // do some stuff
      return doSomethingElseEntirelyAsync(something); // returns a promise
    });
}

Turns to

async function asyncFunc() {
  await doSomethingAsync(); // awaits for a promise
  // do some stuff
  let something = await doSomethingElseAsync(); // awaits for a promise
  // do some stuff
  return doSomethingElseEntirelyAsync(something); // returns the final promise
  // Note that even if you return a value, like return 5, the function as a whole
  // still returns a promise!
}

It reads a lot better, and you can use the normal tools like try/catch and for loops to work with them, even though they're async.

Async functions are NOT a replacement for promises, they're sugar on top of them, to handle specific cases where you have many sequential asynchronous actions.

Because await is basically just "await for this promise", you can still use the cool aggregation methods like Promise.all() and Promise.race() and await for the result of several (or the first of several) promises.

I'm not familiar with a way of distinguishing the two in runtime because, like classes, async functions are just sugar on top of Promises. (Although there might be hacks like using the function's .toString and parse the results, I don't count those).

Upvotes: 9

Related Questions