Async & Await: JS Magic
Async Keyword
- Async functions always return a promise.
- If the function returns a value, the promise will be resolved with that value
- If the function throws an exception, the promise will be rejected
// REGULAR function returns a string:
// function greet() {
// return 'HELLO!!!';
// }
// THE ASYNC KEYWORD!
// This function now returns a promise!
async function greet() {
return 'HELLO!!!'; //resolved with the value of 'HELLO!!!'
}
greet().then((val) => {
console.log('PROMISE RESOLVED WITH: ', val);
});
async function add(x, y) {
if (typeof x !== 'number' || typeof y !== 'number') {
throw 'X and Y must be numbers!';
}
return x + y;
}
// Equivalent non-async function...
// function add(x, y) {
// return new Promise((resolve, reject) => {
// if (typeof x !== 'number' || typeof y !== 'number') {
// reject('X and Y must be numbers!');
// }
// resolve(x + y);
// });
// }
add(6, 7)
.then((val) => {
console.log('PROMISE RESOLVED WITH: ', val);
})
.catch((err) => {
console.log('PROMISE REJECTED WITH: ', err);
});
The Await Keword
- We can only use the awit keyword inside of functions declared with async
- await will pause the execution of the function, waiting for a promise to be resolved
async function getPlanets() {
const res = await axios.get('https://swapi.dev/api/planets/');
console.log(res.data); //only runs once the previous line is complete (the axios promise is resolved)
}
getPlanets();
// Without async/await...
// function getPlanets() {
// return axios.get('https://swapi.dev/api/planets/');
// }
// getPlanets().then((res) => {
// console.log(res.data);
// });
Error Handling in Async Functions
// ONE OPTION FOR ERROR HANDLING...
async function getPlanets() {
//Invalid URL...
const res = await axios.get('https://swapi.dev/api/planeklsajdalksts/');
console.log(res.data);
}
getPlanets().catch((err) => {
console.log('IN CATCH!!!');
console.log(err);
});
// ANOTHER OPTION...
async function getPlanets() {
try {
const res = await axios.get('https://swapi.dev/api/planeklsajdalksts/');
console.log(res.data);
} catch (e) {
console.log('IN CATCH!', e);
}
}
getPlanets();
Multiple Awaits
const moveX = (element, amount, delay) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const bodyBoundary = document.body.clientWidth;
const elRight = element.getBoundingClientRect().right;
const currLeft = element.getBoundingClientRect().left;
if (elRight + amount > bodyBoundary) {
reject({ bodyBoundary, elRight, amount });
}
else {
element.style.transform = `translateX(${currLeft + amount}px)`;
resolve();
}
}, delay);
});
};
const btn = document.querySelector('button');
async function animateRight(el, amt) {
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
await moveX(el, amt, 1000);
}
animateRight(btn, 100).catch((err) => {
console.log('Hit the right edge! Now Moving left!');
animateRight(btn, -100);
});
// const btn = document.querySelector('button');
// moveX(btn, 100, 1000)
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .then(() => moveX(btn, 100, 1000))
// .catch(({ bodyBoundary, amount, elRight }) => {
// console.log(`Cannot Move! Body is ${bodyBoundary}px wide`);
// console.log(`Element is at ${elRight}px, ${amount}px is too large!`);
// });
//This function moves an element "amount" number of pixels after a delay.
//If the element will stay on screen, we move the element and call the onSuccess callback function
//If the element will move off screen, we do not move the element and instead call the onFailure callback
// const moveX = (element, amount, delay, onSuccess, onFailure) => {
// setTimeout(() => {
// const bodyBoundary = document.body.clientWidth;
// const elRight = element.getBoundingClientRect().right;
// const currLeft = element.getBoundingClientRect().left;
// if (elRight + amount > bodyBoundary) {
// onFailure();
// }
// else {
// element.style.transform = `translateX(${currLeft + amount}px)`;
// onSuccess();
// }
// }, delay);
// };
// LOOK AT THIS UGLY MESS!
// moveX(
// btn,
// 300,
// 1000,
// () => {
// //success callback
// moveX(
// btn,
// 300,
// 1000,
// () => {
// //success callback
// moveX(
// btn,
// 300,
// 1000,
// () => {
// //success callback
// moveX(
// btn,
// 300,
// 1000,
// () => {
// //success callback
// moveX(
// btn,
// 300,
// 1000,
// () => {
// //success callback
// alert('YOU HAVE A WIDE SCREEN!');
// },
// () => {
// //failure callback
// alert('CANNOT MOVE FURTHER!');
// }
// );
// },
// () => {
// //failure callback
// alert('CANNOT MOVE FURTHER!');
// }
// );
// },
// () => {
// //failure callback
// alert('CANNOT MOVE FURTHER!');
// }
// );
// },
// () => {
// //failure callback
// alert('CANNOT MOVE FURTHER!');
// }
// );
// },
// () => {
// //failure callback
// alert('CANNOT MOVE FURTHER!');
// }
// );
Parallel vs. Sequential Requests
// SEQUENTIAL REQUESTS!
async function get3PokemonSequential() {
const poke1 = await axios.get('https://pokeapi.co/api/v2/pokemon/1');
const poke2 = await axios.get('https://pokeapi.co/api/v2/pokemon/2');
const poke3 = await axios.get('https://pokeapi.co/api/v2/pokemon/3');
console.log(poke1.data);
console.log(poke2.data);
console.log(poke3.data);
}
// PARALLEL REQUESTS!
async function get3Pokemon() {
const prom1 = axios.get('https://pokeapi.co/api/v2/pokemon/1');
const prom2 = axios.get('https://pokeapi.co/api/v2/pokemon/2');
const prom3 = axios.get('https://pokeapi.co/api/v2/pokemon/3');
const poke1 = await prom1;
const poke2 = await prom2;
const poke3 = await prom3;
console.log(poke1.data);
console.log(poke2.data);
console.log(poke3.data);
}
get3Pokemon();
// *******************************************
// A better demonstration of the difference...
// *******************************************
function changeBodyColor(color, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
document.body.style.backgroundColor = color;
resolve();
}, delay);
});
}
// IN SEQUENCE
async function lightShow() {
await changeBodyColor('teal', 1000);
await changeBodyColor('pink', 1000);
await changeBodyColor('indigo', 1000);
await changeBodyColor('violet', 1000);
}
// IN PARALLEL...
// Everything is "sent off" at the same time
// async function lightShow() {
// const p1 = changeBodyColor('teal', 1000);
// const p2 = changeBodyColor('pink', 1000);
// const p3 = changeBodyColor('indigo', 1000);
// const p4 = changeBodyColor('violet', 1000);
// await p1;
// await p2;
// await p3;
// await p4;
// }
lightShow();
Refactoring with Promise.all -> Parallel Requests
// PARALLEL REQUESTS!
async function get3Pokemon() {
const prom1 = axios.get('https://pokeapi.co/api/v2/pokemon/1');
const prom2 = axios.get('https://pokeapi.co/api/v2/pokemon/2');
const prom3 = axios.get('https://pokeapi.co/api/v2/pokemon/3');
// With promise.all you can wait till all the promises have been resolved
const results = await Promise.all([ prom1, prom2, prom3 ]);
printPokemon(results);
}
function printPokemon(results) {
for (let pokemon of results) {
console.log(pokemon.data.name);
}
}
get3Pokemon();