I thought I understood how to use async and await till I saw the below code snippet.
So there is an onInput event handler function attached to the movie input textbox. Within it is called the fetchData asynchronous function which uses the await keyword to wait for results from the axios.get function.
My question is, why do we need to make the onInput function also async? I mean, the fetchData function is async. Which means, it will wait till the axios.get is resolved and the execution will be paused till axios.get is resolved. So when the line const movies = fetchData(event.target.value); executes, the fetchData function will be executed which will pause on the await axios.get statement. So why do we need to use await while calling fetchData and make onInput async??
const fetchData = async(searchTerm)=>{
const results = await axios.get('http://www.omdbapi.com/', {
params:{
apikey:'d9835cc5',
s: searchTerm
}
});
return results.data.Search;
}
const movieInput = document.querySelector('input');
const onInput = async event => {
const movies = await fetchData(event.target.value);
console.log(movies);
}
movieInput.addEventListener('input', onInput);
I mean, the fetchData function is async. Which means, it will wait till the axios.get is resolved and the execution will be paused till axios.get is resolved.
This concept is hiding a lot of detail that may be confusing, such as that paused execution is resumed using callback functions.
async functions do not pause execution of their callers.
Instead they return a promise that will be resolved with the value syntactically returned from executing the async function body. To be clear, the value apparently returned from within an async function body is not returned to the caller directly - the caller gets a promise for it.
When executed the await operator sets up call backs for when its operand promise becomes settled. Effectively it calls the then method of its promise operand to supply a set of onfulfilled and onrejected callbacks, before storing the current execution context in memory and returning to the event loop.
When it receives a callback from promise settlement, the await operator restores the execution context it previously saved. If the awaited promise is rejected, await throws the rejection reason. If fulfilled, await resumes exection and returns the fulfilled value of the promise as the result of executing the await operator.
Now historically await was never a reserved keyword - not mentioned in ES3, future reserved keyword in ES6 (ECMAScript 2015) but reserved word in the current draft of ECMAscript as at May 2021.
Hence, to retain compatibility with code on the web, await was only recognized as an operator if it occurs within an async function - leaving it available as an identifier outside of async function bodies.
Likewise the async keyword was not historically reserved, but was able to be introduced without comparability issues by requiring its usage in a position that would have produced an unexpected identifier error in previous versions of JavaScript. Meaning before the function keyword or before an arrow function expression.
Finally you need to declare onInput as an async function because await is being used as an operator within its body. Without the async declaraton, await will be treated as an identifier rather than the name of an operator.
As a side note, the promise returned by onInput is being discarded and could generate an uncaught promise rejection error in its current form.
To answer the slightly different question of "why does await need to be used in the onInput function at all?", the value returned from fetchData is a pending promise. In order to set movies to the results.data.Search value obtained within fetchData, you need to wait for the fulfilled value of the returned promise. Using async/await is one means of doing so. The other is to add a fulfillment handler to the promise returned from fetchData by calling its then method.
You are using the await operator inside inInput. It's a JavaScript rule that functions using await must be async, so inInput must also be async. Have a look at this answer: https://stackoverflow.com/a/56685416/13357440
Also, MDN has useful information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
In general, you would want API calls to execute while the rest of your program runs, so the onInput would be async. Furthermore, javascript mandates that an await is inside of an async function, so the code was simply following its standards and rules as well. Typically, once the top level is an async, everything inside of it is just following the structure. More info can be found here.
Related
I have a simple function that aims to log a user in, and some guard clauses in it to rule out errors
async signIn(email: string, passwort: string): Promise<void> {
const user: IPerson = await this.fetchUser(email);
if (user === undefined) {
return Promise.reject('Wrong email');
}
if (user.passwort !== passwort) {
return Promise.reject('Wrong password');
}
if (!this.logInUser(user)) {
return Promise.reject('Login Failed');
}
return Promise.resolve();
}
I used async await to wait for the promise resolve of fetchUser giving me the user, but this made me think, what benefits does await have here?
As summarized in some blog this is how async works:
"Let’s emphasize: await literally suspends the function execution until the promise settles, and then resumes it with the promise result. That doesn’t cost any CPU resources, because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc."
But in my case, there is no other other jobs in the meantime, the function can only resolve if the fetchUser provides something. Doesnt this mean JS Engine automatically waits and behaves just the same like using async? That would make it redundant.
what benefits does await have here?
It lets you write syntax that is easier to follow than using then with a callback.
That's all.
But in my case, there is no other other jobs in the meantime, the function can only resolve if the fetchUser provides something.
fetchUser is asynchronous. It isn't made asynchronous by the use of await. It provides a promise.
If you don't await that promise then you are just changing where the code execution is suspended.
Instead of signIn being suspended and the code that (you say) does nothing outside it continuing, you'll instead only suspend fetchUser and signIn will continue processing with the promise returned by fetchUser instead of the User object that that promise eventually resolves with.
Doesn't this mean JS Engine automatically waits and behaves just the same like using async? That would make it redundant.
No, the JS engine doesn't automatically "wait". Without await it would immediately continue executing the if statements, which obviously is not desired, as then you would not have the actual user object to work with, but "just" a promise object.
If the only thing that you wanted to do was to call fetch then it would be redundant to await it, but since you need the response from that request, you need to get informed when that response is available, and that is what await is offering you. You could alternatively chain a then call, whose callback would be called when the response becomes available.
According to MDN Web Docs, "await" is used to "wait for a Promise", but the
expression following the keyword can also be "any value to wait for", hence not
necessarily a Promise.
In the below demo the awaited value is a call to a void function, i.e. a
function that implicitly returns "undefined". Note: the called function does not
return a Promise. What exactly does it mean to await a void function call?
Demo:
Add keyword "await" to the beginning of line 11, and see how it changes logging
from "FC" to "CF".
"use strict";
function pr() {
return Promise.resolve(null);
}
function invokeAsyncOperation(cb) {
pr().finally(cb);
}
async function main() {
try {
// demo: await the following statement
invokeAsyncOperation(() => console.log("C"));
} catch (e) {} finally {
console.log("F");
}
}
main();
You're seeing the result of two things:
From the docs you linked:
If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise.
Loosely speaking,¹ await x where x is not a promise is the same as await Promise.resolve(x). So when you use await, there's always a promise involved.
Promise completion handlers are always called asynchronously, x and await x have different timing: The former is synchronous; the latter is asynchronous:
A promise is created, fulfilled with x.
await puts a completion handler on the promise.
Since the promise is already fulfilled, a call to that completion handler is put in the microtask queue to be handled when microtasks are run at the end of the current task.
When microtasks are run, the promise completion is reached and does the console.log.
You see the log for C before the log for F because the completion handler logging C is already in the microtask queue before the completion handler for F is added, so it gets run first.
¹ Actually, Promise.resolve is always involved, even when x is already a promise. But the extra promise gets optimized away if x is already a native promise (of the same concrete class). You can see the resolve operation in Step 2 of the algorithm for await.
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().
This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 27 days ago.
I just wonder if there is different between those 2 calls by await Promise.all([/* ... /*])
In my performance test, it took the same time.
This is my method to be invoked by the Promise.all:
const makeFetch1 = async () => {
axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
const makeFetch2 = async () => {
return axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
1) await Promise.all([makeFetch1(), makeFetch1(), ...])
2) await Promise.all([makeFetch2(), makeFetch2(), ...])
Both have the same execution time result, what i should choose?
The first one will fire of a request and not return it. so makeFetch1() will result in a Promise that resolves immediately. This is almost certainly never what you want. An async function should await or return inside somewhere, otherwise it may as well be a standard function (that returns nothing).
Even if you don't care about what the request resolves to, you should still use the result of the request (by returning or awaiting, just so you can see if the request succeeds or not - and communicate to the user that there was a problem if it fails)
The second one will fire of a request and return it. so makeFetch1() will result in a Promise that resolves once the request resolves - which is likely to take a number of milliseconds.
There are several major differences and multiple coding mistakes.
First off this code:
const makeFetch1 = async () => {
axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
is an async function that calls axios.get() and then immediately returns a promise that is already resolved and has an undefined resolved value. Nothing here waits for the axios.get() call to finish.
Your second code block:
const makeFetch2 = async () => {
return axios.get("https://jsonplaceholder.typicode.com/todos/1");
};
calls axios.get() and returns the promise that it creates. So, the promise this function returns will be tied to when the axios.get() call completes and the resolved value will be the resolved value of the axios call.
Differences:
You could use the makeFetch2() function to get the result of the axios.get() call (resolved value or reject error). You could not use makeFetch1() to get the result of the axios.get() call because that result is not communicated back to the caller in any way.
Similarly, makeFetch2() will allow the caller to know when the axios operation has completed. makeFetch1() will not.
If the axios.get() operation rejected, then makeFetch1() would result in an unhandled rejection error because nothing is listening for the rejection.
Then, these two:
await Promise.all([makeFetch1, makeFetch1, ...])
await Promise.all([makeFetch2, makeFetch2, ...])
Are both pretty useless because you're not calling any of the functions so nothing is executing. These will both just resolve immediately with a resolved value of an array of function references. None of the axios calls are executed because nothing actually calls makeFetch1() or makeFetch2().
If you meant to actually execute the functions with these two:
await Promise.all([makeFetch1(), makeFetch1(), ...])
await Promise.all([makeFetch2(), makeFetch2(), ...])
Then, the first one just resolves immediately with an array of undefined values as the resolved value because remember the makeFetch1() return value isn't connected in any way to the axios.get() call.
The second one will properly wait for all the axios.get() calls in makeFetch2() to complete and will resolve with an array of values from the axios.get() calls.
Both have the same execution time result, what i should choose?
That's because as you've implemented them, you aren't calling the makeFetchN() functions in either case. So, they aren't doing anything and thus have the same execution time.
If you change the implementation to actually call the functions as in:
await Promise.all([makeFetch1(), makeFetch1(), ...])
await Promise.all([makeFetch2(), makeFetch2(), ...])
There will be a significant timing difference because the one based on makeFetch1() does not wait for any of the axios.get() calls to finish and does not communicate back results or completion and is subject to uncaught rejections.
The one based on makeFetch1() has no practical use as you could just do:
makeFetch1();
makeFetch1();
...
with the exact same result because makeFetch1() just returns an immediately resolved promise so passing them all to Promise.all() isn't doing anything useful.
makeFetch1() is probably just not useful since it doesn't inform the caller of completion, error or resolved value.
i.e.
async asyncfunction(){
try{
await method1();
await method2();
}
catch(error){
console.log(error);
}
}
Given method1() and method2() are asynchronous functions. Should there be a try/catch block for each await method? Is there an even cleaner way to write this? I'm trying to avoid '.then' and '.catch' chaining.
Using one try/catch block containing multiple await operations is fine when waiting for promises created on the right hand side of the await unary operator:
The await operator stores its parent async functions' execution context and returns to the event loop. Execution of the await operator resumes when it is called back with the settled state and value of its operand.
Upon resumption, await restores the previously saved execution context and returns the operand promise's fulfilled value as the result of the await expression, or throws the rejection reason of a rejected operand.
The try/catch block invocation is part of the execution context both before and after being saved and restored. Hence multiple await operations do not disturb the behavior of an outer try block they share. The catch block will be invoked with the rejection reason of any promise awaited in the try block that is rejected.
If however code awaits multiple existing promises in the same try/catch block and more than one of the promises rejects, an uncaught promise rejection error is generated for all but the first rejection. Thanks to #EyolRoth for supplying this caveat, please read his entire answer in conjunction with this one.
While #traktor answer is correct, I want to add a very important caveat.
The following code snippet is unsafe:
async function foo() {
try {
const p1 = method1();
const p2 = method2();
await p1;
await p2;
} catch (e) {
console.log(e);
}
}
If both promises are rejected, the code will result in UnhandledPromiseRejectionWarning in NodeJS, potentially killing the process if the option --unhandled-rejections is set to throw (which will become the default behavior from Node 15).
Why is this happening?
In the original code of the question, the async methods are invoked sequentially; i.e, if the first method fails (promise rejected), the second method is never invoked.
However, the code from the beginning of this answer invokes both of the async methods in parallel; i.e, the second method is invoked regardless if the first method succeeds or not.
In this scenario, only the await on the promise of the second method is conditioned on the success of the first method. Meaning, if the first method fails, the promise of the second method (which has already started) is never awaited, so if it fails too, it will result in an handled promise rejection.
How to overcome this behavior?
Assuming we wish to handle both rejections in the same way, we can use Promise.all:
try {
const p1 = method1();
const p2 = method2();
await Promise.all([p1, p2]);
} catch (e) {
console.log(e);
}
Only the first promise to be rejected will "trigger" the catch clause, while the second will be silently ignored without crashing the process.
Using Async.js Library
Let's talk about working with the async.js library in order to avoid callback hell.
As per the official website of async.js : Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript.
Async.js provides near about 70 functions in total. For now, we will discuss only two of them i.e, async.waterfall() and async.series().
async.waterfall()
This method is useful when you want to run some tasks one after the other and then pass the result from the previous task to the next task. It takes an array of functions "tasks" and a final "callback" function that is called after all functions in "tasks" array have completed or a "callback" is called with an error object.
async.js library