Reputation: 13121
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()
.
Is a async funtion
a thing of it's own and await
is simply compatible with promises?
and, is there a way to distinguish between a simple function
and an async function
at runtime (in a Babel-compatible way)?
Upvotes: 7
Views: 1612
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.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
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