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)?
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).
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.
Related
I've researched many many posts and articles, and documentation. I'm seeking deeper understanding.
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
The await expression causes async function execution to pause until a Promise is settled, [...]
let fetchLocation = () => {
//fetches information from satellite system and
//returns a Vector2 of lat long on earth
return new Promise(resolve => {
setTimeout(() => {
resolve({
lat: 73,
long: -24
});
}, 2000);
});
}
//async functions automatically return a promise.
let main = async() => {
//awaits the promises resolution
let result = await fetchLocation();
console.log("I'm the awaited promise result", result)
//immediately returns a promise as promise pending
return result;
}
console.log(main().then(res => console.log("I'm the original promise, just passed along by the async function, it didn 't a-wait for me", res)))
Before going down this rabbit hole, I knew async/await was syntactical sugar to make async code look synchronous.
I therefore fully expected to see main return the lat long coordinates. Instead it returns a promise.
A couple questions.
Is the MDN documentation wrong? Does it not actually pause execution? It seems that the await keyword does not actually "pause" function execution... invoking main immediately returns a Promise<pending> object. However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop? (And an async return statement is still synchronous.)
So it seems that await unwraps a promise, and async wraps a promise, but you need to use await within async. I get that await might actually be promise.then() underneath the hood but if they want to make it fully synchronous in appearance, why not have the async function return the resolved promise value when used with await? Otherwise async await seems a bit redundant.
Is the MDN documentation wrong?
No
Does it not actually pause execution?
It pauses the execution of the async function, hands a Promise back to the calling function — because it hasn't reached the point in the async function where it knows what to return — then the execution of the calling function (and the rest of the code) continues.
However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop?
When the event loop stops being busy (running the code that called the async function in the first place and anything else that it picks up in the meantime) and the promise that was being awaited resolves, then the async function is woken up and given the resolved value of the function to continue processing.
why not have the async function return the resolved promise value when used with await?
Because that would block the entire event loop, which is why asynchronous logic (starting with callback style APIs) was introduced into JS in the first place.
Otherwise async await seems a bit redundant.
Consider a situation where you want to request a number of URLs from an API serially (so that you don't shove too many simultaneous requests at the API):
const data = [];
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const response_data = await response.json();
data.push(response_data);
}
If you were to rewrite that without await then you'd probably end up with something recursive to count your way through the loop. It would be significantly more complex.
async/await makes dealing with complex combinations of promises simple, and simple promises trivial.
knew async/await was syntactical sugar to make async code look synchronous.
And it does that inside the async function and only inside it.
It doesn't really pause the function, it just allows you to write the rest of the function code as if it were. But it's really just wrapping the rest of the code of the function into a .then().
E.g.
await foo = someFunc();
console.log(foo);
is executed like
someFunc().then((foo => {
console.log(foo);
});
Since it's really still asynchronous, you can't return the resolved value, because the original calling function returns immediately. But if the calling function is also declared async, it returns a Promise, and the caller can either use await again (so it appears to be synchronous) or use .then().
I have the following code. performAsyncAction performs an async action and returns Promise<Response>. In someFunction , I was surprised that TypeScript doesn't warn about not using await on a function that returns a promise.
function performAsyncAction() {
return fetch('someservice');
}
function someFunction() {
const result = performAsyncAction(); // Was expecting typescript to give error here
}
I found a relevant linting rule that may help promise-function-async
It is not an error, it is intended behaviour.
The Promises existed long before async/await, you have several ways how to handle them
Awaiting promise is internal logic of function, not requirement
Typescript knows well that you have Promise and not the value inside that Promise, so it will warn you if you want to use it in a wrong way
The function you have provided is not async, therefore it is not even possible to await there
await/async and Promises are the same thing in Javascript, it does not matter if you await Promise or if you .then Promise, its only syntactic sugar (useful one though)
There is a lot of cases where you dont want to await newly created Promise, i.e. to allow processing of several Promises at once, thus reducing the time of processing request
I absolutely agree with you. In my experience forgetting about awaiting a promise is the source of majority of problems, especially with handling errors.
Although await/async syntax exists in pure JS, it would make perfect sense to introduce another syntax sugar that would notify the TS compiler you intend to leave the promise to run 'in-the-background'.
This, as any of the other strict checks, would be optional.
E.g.
async function someFunction() {
const result = performAsyncAction(); // Compilation error
}
async function someFunction() {
const result = along performAsyncAction(); // Returns promise
}
async function someFunction() {
const result = await performAsyncAction(); // Awaits the promise
}
This question already has answers here:
Difference between `return await promise` and `return promise`
(7 answers)
Closed 4 years ago.
For the most part, I think I understand how async functions work in JavaScript / Node.js, and I am familiar with the syntax of async/await. But this question is about a very specific case, which I can’t seem to find anywhere, and I think it involves the inner workings of await, which is beyond my understanding.
First I’ll start off with a simple example, and then I’ll extend it to real working code. (Note: I’m actually using Typescript, so you’ll see void instead of undefined, but that’s not important to the problem.)
async function subtask() {
// doSomeStuff();
return; // returns a `Promise<void>`
}
// async function run() {
// see options below
// }
function app() {
run().catch((e) => console.error(e));
}
Option 1
async function run() {
await subtask(); // resolves to `void`
return; // returns a new `Promise<void>`
}
Option 2
async function run() {
return subtask(); // returns a new Promise, resolving to the `Promise<void>` of `subtask()`
}
In the simple example above, I have an async function run that calls a smaller async function subtask. Both functions must return a Promise<void>. I have two options: (1) to await the smaller function and return a new Promise<void>, or (2) to return a wrapped promise, given by the smaller function, which will later resolve to void.
My lack of understanding is about how this works. In Option 1, is execution is paused before subtask() returns? What does that actually mean? Does that mean that the async subtask executes synchronously? Does that mean that app, which calls run(), will also pause execution? What if app was async, would that make a difference?
Is it “better” (more performant) to let the promise bubble up and resolve later, or to resolve it directly within the run function?
The reason this is important is that in my real code I have a bunch of smaller subtasks, which all return void, and then the big function must also return void—it cannot return an array. (Note that the subtasks need not run in any particular order.)
Option 1
async function run() {
await Promise.all([
subtask0(),
subtask1(),
subtask2(),
]);
return;
}
Option 2
async function run() {
return Promise.all([
subtask0(),
subtask1(),
subtask2(),
]).then((_all) => {});
}
function app() {
// do some stuff
run(); // if `run` contains `await`, does execution pause here?
// what if `app` was async?
// do some more stuff
}
It is always better to let the Promise bubble up. This avoids the creation of an extra Promise object that will also be awaited on (whether this is optimized away behind the scenes in whichever JavaScript engine you're using is up to debate, though).
async function run() {
await subtask();
return;
}
This creates an extra Promise (and subsequently an extra callback in the chain that will be executed).
async function run() {
return subtask();
}
This isn't actually doing what you think it is. This is also creating an extra Promise (since you're using the async keyword) and is almost functionally the same as the previous example. By using the async keyword, you're creating and returning a new Promise that will resolve/reject to the same value as the Promise created from the call to subtask(). If you remove the async keyword, then this will avoid the creation of the extra unnecessary Promise .
Now with your Promise.all() examples, I think they're both optimal (assuming you remove the unnecessary async keyword from the 2nd one, like noted above). The first would create 2 Promise s (the Promise.all() one, and the one created from returning from an async function), and the 2nd would as well (the Promise.all() one, and the one created from calling then()). Whichever you'd want to use is basically up to personal choice. Personally, I like the 2nd example since it's not mixing the use of async functions and Promises, which I think makes it a little easier to understand.
And for the last part of your question, execution will be paused wherever the await keyword exists (so within the run() call).
You can also essentially think of the await keyword transforming your code from this:
// Do some stuff
let result = await run();
// Do some other stuff
to this:
// Do some stuff
run().then(result => {
// Do some other stuff
};
(the whole point of await is to reduce confusion/complexity caused by nesting)
I am just getting up to speed on Async/Await in Typescript.
I am converting existing code like this:
getImportanceTypes(): Promise<void> {
return this.importanceTypeService.list()
.then(items => {
this.importanceTypes = items;
});
}
to:
async getImportanceTypes(): Promise<void> {
this.importanceTypes = await this.importanceTypeService.list()
}
The question is: Does this really return a promise? It must, because it compiles successfully, but in my mind, I see the code execution suspending on the await until it completes, then continuing.
The reason I ask is that I have about 10 similiar calls to the above (different type tables) and I would like them to execute in parallel with Promise.all.
Yes, async functions return promises. (In JavaScript and TypeScript both.) async/await is "just" syntactic sugar for the creation and consumption of promises (but, you know, really useful sugar).
The way you've declared that return type is indeed the correct way to do it. There's been some dissention on that point, however. :-) There are some who feel that if the function is declared async, you should be able to just specify its resolution type rather than explicitly mentioning the promise. At present, though, you do use Promise<x> rather than just x.
I am confused about the current discussion of adding async functions and the keyword await to the next EcmaScript.
I do not understand why it is necessary to have the async keyword before the function keyword.
From my point of view the await keyword to wait for a result of a generator or promise done, a function's return should be enough.
await should simple be usable within normal functions and generator functions with no additional async marker.
And if I need to create a function what should be usable as an result for an await, I simply use a promise.
My reason for asking is this good explanation, where the following example comes from:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
It also could be done as normal function, if the execution of a function will wait for finishing the hole function until all awaits are fulfilled.
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
In my opinion the whole function execution is holding until the next tick (await fulfillment) is done. The difference to Generator-Function is that the next() is triggering and changing the object's value and done field. A function instead will simple give back the result when it is done and the trigger is a function internal trigger like a while-loop.
I do not understand why it is necessary to have the async keyword before the function keyword.
For the same reason that we have the * symbol before generator functions: They mark the function as extraordinary. They are quite similar in that regard - they add a visual marker that the body of this function does not run to completion by itself, but can be interleaved arbitrarily with other code.
The * denotes a generator function, which will always return a generator that can be advanced (and stopped) from outside by consuming it similar to an iterator.
The async denotes an asynchronous function, which will always return a promise that depends on other promises and whose execution is concurrent to other asynchronous operations (and might be cancelled from outside).
It's true that the keyword is not strictly necessary and the kind of the function could be determined by whether the respective keywords (yield(*)/await) appear in its body, but that would lead to less maintainable code:
less comprehensible, because you need to scan the whole body to determine the kind
more errorprone, because it's easy to break a function by adding/removing those keywords without getting a syntax error
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
That sounds like you want a blocking function, which is a very bad idea in a concurrent setting.
By marking a function as async, you're telling JS to always return a Promise.
Because it will always return a Promise, it can also await on promises inside of its own block. Imagine it like one giant Promise chain - what happens internally to the function gets effectively gets bolted on to its internal .then() block, and what's returned is the final .then() in the chain.
For example, this function...
async function test() {
return 'hello world';
}
... returns a Promise. So you can execute it like one, .then() and all.
test().then(message => {
// message = 'hello world'
});
So...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
Is roughly analogous to...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
In both cases, the callback passed to test().then() will receive report as its first parameter.
Generators (i.e. marking a function * and using the yield keyword) are a different concept altogether. They don't use Promises. They effectively allow you to 'jump' between different portions of your code, yielding a result from inside of a function and then jumping back to that point and resuming for the next yield block.
Although they feel somewhat similar (i.e. 'halting' execution until something happens somewhere else), async/await only gives you that illusion because it messes with the internal ordering of Promise execution. It's not actually waiting - it's just shuffling when the callbacks happen.
Generators, by contrast, are implemented differently so that the generator can maintain state and be iterated over. Again, nothing to do with Promises.
The line is further blurred because at the current time of writing, support for async/await is scare; Chakracore supports it natively, and V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await and convert the code to generators. It's a mistake to conclude that generators and async/await are therefore the same; they're not... it just so happens that you can bastardize how yield works alongside Promises to get a similar result.
Update: November 2017
Node LTS now has native async/await support, so you ought never to need to use generators to simulate Promises.
These answers all give valid arguments for why the async keyword is a good thing, but none of them actually mentions the real reason why it had to be added to the spec.
The reason is that this was a valid JS pre-ES7
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
According to your logic, would foo() return Promise{42} or "awaiting 42"? (returning a Promise would break backward compatibility)
So the answer is: await is a regular identifier and it's only treated as a keyword inside async functions, so they have to be marked in some way.
The reason for the async keyword in front is simple so you know that return value will be transformed into a promise. If no keyword how would the Interpreter know to do this.
I think this was first introduce in C# and EcmaScript is taking a loot of stuff from TypeScript. TypeScript and C# are conceived by Anders Hejlsberg and are similar.
lets say you have a function (this one is just to have some asynchronous work)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
this function will make us wait for a random time and return a Promise (if you use jQuery Promise is similar to Deferred) object. To use this function today you would write something like this
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
And this is fine. Now lets try to return the log message
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
Ok this is not bad but there are two return statements and a function in the code.
Now with async this will look like this
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
The code is short and inline. If you written a lot of .then() or . done() you know how unreadable the code can get.
Now why the async keyword in front of function. Well this is to indicate that your return value is not what is returned. In theory you could write this(This can be done in c# I don't know if js will allow since it's not done).
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
you see, you return a number but the actual return will be a Promise you don't have to use
return new Promise(function(resolve, reject) { resolve(5);};
Since you can't await a number only a Promise await test(false) will throw an exception and await test(true) will not if you don't indicate async in front.