There's a special syntx to work with promises in a more comfortable fashion, called async/await
. It's surprisingly easy to understand and use.
Let's start with the async
keyword. It can be placed before a function, like this:
async function f() {
return 1;
}
The word async
before a function means one simple thing: a function always returns a promise. Other values are wrapped with a resolved promise automatically.
for instance, this function returns a resolved promise with the result of 1
; let's test it:
async function f() {
return 1; // equivalent to return Promise.resolve(1);
}
f().then((data) => console.log(data));
1
So, async
ensures that the function returns a promise, and wraps non-promises in it.
The keyowrd await
makes sure javascript wait unitl promise settles and returns a result. await
works only inside async function.
The syntax:
let value = await promise;
Fetching a post from jsonplaceholder using async/await:
async function f2() {
let promise_object_post_one = fetch(`https://jsonplaceholder.typicode.com/posts/1`);
let response = await promise_object_post_one; // (#1)
let data = await response.json(); // (#2)
console.log(data.title);
}
f2();
sunt aut facere repellat provident occaecati excepturi optio reprehenderit
The function execution "pauses" at line (#1)
and resumes when the promise settles, with the response. Then again the function execution "pauses" at line (#2)
and resume when the proise settles with the data.
That doesn't cost any CPU resources, because the Javascript engine can do other jobs in the meantime.
Its just a more elegant syntax of getting the promise result than promise.then
, easier to read and write.
We can catch that error using try..catch
Example:
async function f3() {
try {
let response = await fetch('http://no-such-url');
} catch (err) {
console.log(err); // TypeError: failed to fetch
}
}
f3();
If we dont have try..catch
, then the promise generated by the call of the async function f3()
becomes rejected. We can append .catch
to handle it.
f3()
.then((response) => {console.log(response)})
.catch((err) => {console.log(err)})
TypeError: failed to fetch
if we forget to add .catch
even there, then we get unhandled promise error. We can catch such error using a global unhandledrejection
event handler.
async function moviePlanets (movieNum) {
let films_url = 'https://swapi.dev/api/films/';
let movie_response = await fetch(films_url + movieNum + '/');
let movie_data = await movie_response.json();
console.log('Movie title: ', movie_data.title);
let planets = movie_data.planets;
let planets_promise_array = planets.map((url) => fetch(url).then((response) => response.json()));
// normal loops like for..each doesnot work.
for await (let pl of planets_promise_array) {
console.log('planet: ', pl.name);
}
}
moviePlanets(1);
We can use throw
to create an exeption if we need one, if we are using the try..catch mechanism.
async function moviePlanets(movieNum) {
let films_url = 'https://swapi.dev/api/films/';
try {
if (typeof movieNum !== 'number') {
// We can use throw to create an exeption if we need one.
throw "You must pass in a number.";
}
let movie_response = await fetch(films_url + movieNum + '/');
let movie_data = await movie_response.json();
console.log('Movie title: ', movie_data.title);
let planets = movie_data.planets;
let planets_promise_array = planets.map((url) => fetch(url).then((response) => response.json()));
// normal loops like for..each doesnot work.
for await (let pl of planets_promise_array) {
console.log('planet: ', pl.name);
}
} catch (e) {
console.error('Error: ', e)
}
}
moviePlanets(1);
We can use an IIFE to do that:
(async function () {
let response = await fetch(`https://jsonplaceholder.typicode.com/todos/1`);
let obj = await response.json();
console.log(obj);
})();
let blog_posts = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3'
];
let blog_posts_promise_objects = blog_posts.map((post) => fetch(post));
(async function () {
let responses = await Promise.all(blog_posts_promise_objects);
console.log(responses);
})();
Just use the async
keyword before the object method:
let user_obj = {
first_name: 'John',
last_name: 'Doe',
async get_todo() {
let response = await fetch(`https://jsonplaceholder.typicode.com/todos/1`);
let obj = await response.json();
console.log(obj);
}
}
user_obj.get_todo();
Just use the keyword async
before the class method:
class Todos {
async get_todo() {
let response = await fetch(`https://jsonplaceholder.typicode.com/todos/1`);
let obj = await response.json();
console.log(obj);
}
}
var myTodo = new Todos();
myTodo.get_todo();
Keep async functions small or we may end up blocking the execution of code that need not be blocked. Remeber, async/await pattern allows us to use async code. It does not cause your code to become asynchronous.
The async
keyword before a function has two effects:
await
to be used in it.