Promises
An asynchronous operation allows the computer to move on to other tasks while waiting for the operation to be completed. Promises are objects that represent the eventual outcome of an asynchronous operation. These can be in one of three states:
Pending: The initial state when the operation hasnΒ΄t completed yet
Fulfilled: The operation has completed successfully, and the promise stores a resolved value
Rejected: The operation has failed, and the promise stores a reason for the failure
A promise is called settled when it isn't pending, no matter if it was rejected or fulfilled.
To create a new promise, we instantiate an object of the Promise class. To the promise, we pass a function with two parameters, usually called resolve and reject. These will store a resolve and reject function from the Promise class, which will settle based on the results of asynchronous operations. This function is called an executioner function
const func = (resolve, reject) => { // The executioner function
if (true) { // Ab example with a simple condition
resolve('I resolved!');
} else {
reject('I rejected!');
}
}
const myPromise = new Promise(func); // Create a promise with the function
console.log(myPromise);
The
setTimeout()
function lets us execute a function after some time. The other instructions will be executed normally, while the function we pass tosetTimeout()
will be executed after the specified time
console.log("This is executed first");
function greetings(){ // Fuction to be executed after
console.log('This is executed after 2 seconds');
}
setTimeout(greetings,2000); // We pass the function and the value in milliseconds
console.log("This is executed second, even if the instruction is after");
Promise objects have a method
then
that allows us to set instructions to be done after a promise settles. It takes two callback functions as arguments, which are referred to as the onFulfilled handler that should contain the logic when resolved, and the onRejected handler that should contain the logic when rejected. Thethen
function can be invoked with one, both, or neither handler, and its handlers will receive the values returned by theresolve()
andreject()
functions of the promise
const myProm = new Promise((resolve, reject) => {
let num = Math.ceil(Math.random()*10);
if (num <= 5 ){ // Condition checked by the promise
resolve(`Yay ${num} is small`);
} else {
reject(`Oh no ${num} is too big`);
}
});
const onFulfilled = (resolvedValue) => { // Resolve handler
console.log(resolvedValue);
};
const onRejected = (rejectionReason) => { // Reject handler
console.log(rejectionReason);
};
myProm.then(onFulfilled, onRejected); // Then uses the handlers and the promise
Promise objects also have a method
catch
which function is to manage the onRejected handler, instead of calling both handlers withthen
, allowing the separation of responsibilities, which is a better practice
// We transformed the instantiation to a function that returns a promise
// This allows the promise to receive parameters
const myPromise = (myNum) => {
return new Promise((resolve, reject) => {
let num = Math.ceil(Math.random()*10);
if (num <= myNum ){
resolve(`Yay ${num} is small`);
} else {
reject(`Oh no ${num} is too big`);
}
});
};
myPromise(5) // We synthesize the syntax to be more readable
.then((resolvedValue) => { // Then handles the rejected state
console.log(resolvedValue);
})
.catch((rejectionReason) => { // Catch handles the rejected state
console.log(rejectionReason);
})
When we have multiple operations that depend on each other to execute or that must be executed in a certain order, we can chain multiple promises. If any of the promises fail, it will settle to rejected and close all the promises
firstPromise() // The first promise is used
.then((resolvedValue) => {
return secondPromise(resolvedValue); // Returns the second promise with the resolved value of the first promise
})
.then((resolvedValue) => { // Define the second promise
console.log(resolvedValue); // Use resolved value of the second promise
})
.catch((rejectionReason) => { // This happens if any if the promises is rejected
console.log(rejectionReason);
});
When we have multiple operations that donΒ΄t depend on each other or have an order, we can use the
.all()
function to use concurrency and set multiple asynchronous operations. It takes an array of promises and returns a single promise. If any of the promises are rejected, the overall promise is rejected, and if all the promises are resolved, the overall promise too, and all resolved values are returned in an array
let myPromises = Promise.all([promOne(), promTwo(), promThree()]);
myPromises
.then((arrayOfResponses) => { // If resolved, returns and array with all values
console.log(arrayOfValues);
})
.catch((rejectionReason) => { // If any rejected, this is used
console.log(rejectionReason);
});
Async-Await
JavaScript uses an event loop to handle asynchronous function calls. When a program is run, function calls are made and added to a stack. The requests that need to wait for servers to respond are then sent to a separate queue. Once the stack has cleared, the functions in the queue are executed. The event loop is used to create a smoother browsing experience by handling asynchronous functions.
The
async
keyword is used to declare a function that handles a sort of asynchronous actions, which return a promise, and if a non-promise value is returned, it will return a promise resolved to that value, or resolved toundefined
if anything is returned
async function myAsyncFunc() {
return 5;
};
const myFunc = async () => { // Also setting them with arrow functions
return 5
};
myAsyncFunc() // Promise returned
.then(resolvedValue => {
console.log(resolvedValue);
})
The
await
keyword can only be used within anasync
function, and it suspends the execution of the function while waiting for a promise to resolve. It assigns the settled value of the promise instead of returning it completely
let myPromise = () => { // Promise to be used
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Resolved')
}, 1000);
});
}
async function noAwait() {
let value = myPromise(); // Execution is not paused
console.log(value);
}
async function yesAwait() { // Execution is paused until completed the promise
let value = await myPromise();
console.log(value);
}
noAwait(); // Prints Promise { <pending> } as promise wasn't completed
yesAwait(); // Prints Resolved
We can also use multiple
await
statements to simulate the behavior of dependent promises
async function asyncAwaitMultiple(){ // This execute the promises in order
const firstValue = await returnsFirstPromise();
const secondValue = await returnsSecondPromise(firstValue);
const thirdValue = await cookTheBeans(secondValue);
return thirdValue;
}
asyncAwaitMultiple();
We can also use
await
only in the result of the promises to make concurrence in the execution. Instead of waiting for the result of one to execute the other, the promises are started at the same time and executed in parallel, and we only wait for the result
async function waiting() { // Sequential execution
const firstValue = await firstAsyncThing();
const secondValue = await secondAsyncThing();
console.log(firstValue, secondValue);
}
async function concurrent() { // Concurrent execution
const firstPromise = firstAsyncThing();
const secondPromise = secondAsyncThing();
console.log(await firstPromise, await secondPromise);
}
A try-catch statement lets us handle whether the promises have been executed successfully or not. The
try
block will be run if all the promises are settled, and thecatch
statement will run if there was an exception (error)
async function tryCatch() {
try {
let resolveValue = await asyncFunction('thing that will fail');
let secondValue = await secondAsyncFunction(resolveValue);
} catch (err) {
// Catches any errors in the try block
console.log(err);
}
}
tryCatch();
We can also execute promises concurrently using
await
with thePromise.all()
function. This lets to stop the execution as soon as one of the promises is rejected
async function asyncAll() {
const result = await Promise.all([asyncTask1(), asyncTask2(), asyncTask3()]);
for (let i = 0; i < result.length; i++){
console.log(resultArray[i]); // Iterate over resolved values of each promise
}
}
Last updated