Asynchronous JavaScript
Asynchronous JavaScript
c) Chaining Promises:-
One of the most important benefits of Promises is that they provide a natural way toexpress a
sequence of asynchronous operations as a linear chain of then() methodinvocations, without
having to nest each operation within the callback of the previousone.
Promise Chaining is a simple concept by which we may initialize another promise
inside our .then() method and accordingly we may execute our results. The function inside then
captures the value returned by the previous promise.The catch() method is used with the callback
when the promise is rejected or if an error occurs.
Example:-
<script>
letcountValue = new Promise(function (resolve, reject) {
resolve("Promise resolved");
});
// executes when promise is resolved successfully
countValue
.then(function successValue(result) {
console.log(result);
})
.then(function successValue1() {
console.log("You can call multiple functions this way.");
});
</script>
Output:-
Promise resolved
You can call multiple functions this way.
Example:-
letcountValue = new Promise(function (resolve, reject) {
reject('Promise rejected');
});
// executes when promise is resolved successfully
countValue.then(
functionsuccessValue(result) {
console.log(result);
}, )
// executes if there is an error
.catch(
functionerrorValue(result) {
console.log(result);
});
Output:-
Promise rejected
Method Chain:-When more than one method is invoked in a single expression like this,
fetch().then().then()
we call it a method chain. We know that the fetch() function returns a Promise object, and
wecan see that the first .then() in this chain invokes a method on that returnedPromise object. But
there is a second .then() in the chain, which means that the firstinvocation of the then() method
must itself return a Promise.
d) Resolving Promises:-
In order for Promise chains to work usefully, the output of task 2 must become the input to task
3. And in the example we’re considering here, the input to task 3 is the body of the URL that was
fetched, parsed as a JSON object. But, the return value of callback c1 is not a JSON object, but
Promise p4 for that JSON object. This seems like a contradiction, but it is not: when p1 is
fulfilled, c1 is invoked, and task 2 begins. And when p2 is fulfilled, c2 is invoked, and task 3
begins.
But just because task 2 begins when c1 is invoked, it does not mean that task 2 must end when c1
returns. Promises are about managing asynchronous tasks, after all, and if task 2 is asynchronous
(which it is, in this case), then that task will not be complete by the time the callback returns.
Example:-
function c1(response)
{ // callback 1
let p4 = response.json();
return p4; // returns promise 4
}
function c2(profile)
{ // callback 2
displayUserProfile(profile);
}
let p1 = fetch("/api/user/profile"); // promise 1, task 1
let p2 = p1.then(c1); // promise 2, task 2
let p3 = p2.then(c2); // promise 3, task 3
p1 is the Promise returned by the fetch() call. p2 is the Promise returned by the first .then() call,
and c1 is the callback that we pass to that .then() call. p3 is the Promise returned by the
second .then() call, and c2 is the callback we pass to that call. Finally, c3 is the callback that we
pass to the .catch() call.
f) Promises in Parallel:-
Promise.all() takes an array of Promise objects as its input and returns a Promise. The returned
Promise will be rejected if any of the input Promises are rejected. Otherwise, it will be fulfilled
with an array of the fulfillment values of each of the input Promises.
Both Promise.all() and Promise.allSettled() methods are the methods of a Promise object (which
is further a JavaScript object used to handle all the asynchronous operations) that are used to
handle multiple promises results simultaneously.
Promise.all() method:- It returns a single Promise after receiving one or more promises as input.
when all of the Promises in the input are satisfied, the returning promise is fulfilled. when any of
the inputs, or promises are refused, it rejects a promise with this first rejection reason.
Syntax:
Promise.all([promise_1 , promise_2, ...]).then(
// do something...
)
Promise.allSettled() method:- It returns a single Promise from one or more promises that are
passed in as input when all of the input’s promises have been settled, this promise is fulfilled and
an array of objects containing a description of each promise’s result is returned.
Syntax:
Promise.allSettled([promise_1 , promise_2, ...]).then(
// do something...
)
Example:- Promise.all()
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000) );
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 2 resolved"), 500));
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800));
const promisesArray = [promise1, promise2, promise3];
Promise.all(promisesArray)
.then((results) => {
console.log("All promises resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error); });
Output:-
All promises resolved: [ 'Promise 1 resolved', 'Promise 2 resolved', 'Promise 3 resolved' ]
Example:- promise2 resolves after 500 seconds but promise3 rejects after 800 seconds so it
will not wait for promise1 and will return a response:
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000));
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 2 resolved"), 500));
const promise3 = new Promise((_, reject) =>
setTimeout(() => reject("Promise 3 rejected"), 800));
const promisesArray = [promise1, promise2, promise3];
Promise.all(promisesArray)
.then((results) => {
document.write ("All promises resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
Output:-
At least one promise rejected: Promise 3 rejected
Example:- Promise.allSettled()
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000));
const promise2 = new Promise((_, reject) =>
setTimeout(() => reject("Promise 2 resolved"), 500));
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800));
const promisesArray = [promise1, promise2, promise3];
Promise.allSettled(promisesArray).then((results) => {
console.log("All promises settled:", results); });
Output:-
[
{ status: 'fulfilled', value: 'Promise 1 resolved' },
{ status: 'rejected', reason: 'Promise 2 resolved' },
{ status: 'fulfilled', value: 'Promise 3 resolved' }
]
Promise.race():- race returns first promise with shortest delay whether it is resolved or rejected.
For example if there are 5 promises which returns result like this:
Promise 1 ==> 1 second (rejected)
Promise 2 ==> 2 seconds (rejected)
Promise 3 ==> 3 seconds (resolved)
Promise 4 ==> 4 seconds (resolved)
Promise 5 ==> 5 seconds (resolved)
So, it will return us Promise 1 because it was the first one being returned.
Example:-
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 1 resolved"), 1000));
const promise2 = new Promise((_,reject) =>
setTimeout(() => reject("Promise 2 rejected"), 500));
const promise3 = new Promise((resolve) =>
setTimeout(() => resolve("Promise 3 resolved"), 800));
const promisesArray = [promise1, promise2, promise3];
Promise.race(promisesArray)
.then((results) => {
console.log("First promise resolved:", results);
})
.catch((error) => {
console.error("At least one promise rejected:", error);
});
Output:-
At least one promise rejected: Promise 2 rejected
g) Making Promises
Promises based on synchronous values:-
Promise.resolve() takes a value as its single argument and returns a Promise that will
immediately (but asynchronously) be fulfilled to that value. Similarly, Promise.reject() takes a
single argument and returns a Promise that will be rejected with that value as the reason.
When we call Promise.resolve(), we typically pass the fulfillment value to create a
Promise object that will very soon fulfill to that value. If you pass a Promise p1 to
Promise.resolve(), it will return a new Promise p2, which is immediately resolved, but which will
not be fulfilled or rejected until p1 is fulfilled or rejected.
It is possible, but unusual, to write a Promise-based function where the value is computed
synchronously and returned asynchronously with Promise.resolve(). If you detect error
conditions (such as bad argument values) before beginning an asynchronous operation, you can
report that error by returning a Promise created with Promise.reject().Finally, Promise.resolve()
is sometimes useful to create the initial Promise in a chain of Promises.
you invoke the Promise() constructor and pass a function as its only argument. The
function you pass should be written to expect two parameters, which, by convention, should
be named resolve and reject. The constructor synchronously calls your function with function
arguments for the resolve and reject parameters. After calling your function, the Promise()
constructor returns the newly created Promise. That returned Promise is under the control of the
function you passed to the constructor. That function should perform some asynchronous
operation and then call the resolve function to resolve or fulfill the returned Promise or call the
reject function to reject the returned Promise. Your function does not have to be asynchronous: it
can call resolve or reject synchronously, but the Promise will still be resolved, fulfilled, or
rejected asynchronously if you do this.
Example:-
function wait(duration) {
// Create and return a new Promise
return new Promise((resolve, reject) => {
// If the argument is invalid, reject the Promise
if (duration < 0) {
reject(new Error("Time travel not yet implemented"));
}
// Otherwise, wait asynchronously and then resolve the Promise.
// setTimeout will invoke resolve() with no arguments, which means
// that the Promise will fulfill with the undefined value.
setTimeout(resolve, duration);
});
}
h) Promises in Sequence:-
Promise.all() makes it easy to run an arbitrary number of Promises in parallel. And Promise
chains make it easy to express a sequence of a fixed number of Promises. Running an arbitrary
number of Promises in sequence is trickier.
Example:-
function fetchSequentially(urls) {
// We'll store the URL bodies here as we fetch them
const bodies = [];
// Here's a Promise-returning function that fetches one body
function fetchOne(url) {
return fetch(url)
.then(response => response.text())
.then(body => {
// We save the body to the array, and we're purposely
// omitting a return value here (returning undefined)
bodies.push(body);
});
}
// Start with a Promise that will fulfill right away (with value undefined)
let p = Promise.resolve(undefined);
// Now loop through the desired URLs, building a Promise chain
// of arbitrary length, fetching one URL at each stage of the chain
for(url of urls) {
p = p.then(() => fetchOne(url));
}
// When the last Promise in that chain is fulfilled, then the
// bodies array is ready. So let's return a Promise for that bodies array
return p.then(() => bodies);
}
Async and wait:-
a) Await expressions:-
The await keyword takes a Promise and turns it back into a return value or a thrown exception.
Given a Promise object p, the expression await p waits until p settles. If p fulfills, then the value
of await p is the fulfillment value of p. On the other hand, if p is rejected, then the await p
expression throws the rejection value of p. We don’t usually use await with a variable that holds
a Promise; instead, we use it before the invocation of a function that returns a Promise:
let response = await fetch("/api/user/profile");
let profile = await response.json();
Any code that uses await is itself asynchronous.
b) async functions:- you can only use the await keyword within functions that have been
declared with the async keyword.
async function getHighScore() {
let response = await fetch("/api/user/profile");
let profile = await response.json();
return profile.highScore;
}
Declaring a function async means that the return value of the function will be a Promise even if
no Promise-related code appears in the body of the function. If an async function appears to
return normally, then the Promise object that is the real return value of the function will resolve
to that apparent return value. And if an async function appears to throw an exception, then the
Promise object that it returns will be rejected with that exception.
The getHighScore() function is declared async, so it returns a Promise. And because it returns a
Promise, we can use the await keyword with it:
displayHighScore(await getHighScore());
You can use the async keyword with any kind of function. It works with the function keyword as
a statement or as an expression. It works with arrow functions and with the method shortcut form
in classes and object literals.
c) Awaiting Multiple Promises:-
Suppose that we’ve written our getJSON() function using async:
async function getJSON(url) {
let response = await fetch(url);
let body = await response.json();
return body;
}
And now suppose that we want to fetch two JSON values with this function:
let value1 = await getJSON(url1);
let value2 = await getJSON(url2);
The problem with this code is that the fetch of the second URL will not begin until the first fetch
is complete. If the second URL does not depend on the value obtained from the first URL, then
we should probably try to fetch the two values at the same time.
In order to await a set of concurrently executing async functions, we use Promise.all() just as we
would if working with Promises directly:
let [value1, value2] = await Promise.all([getJSON(url1), getJSON(url2)]);
d) Implementation Details
Suppose you write an async function like this:
async function f(x) { /* body */ }
You can think about this as a Promise-returning function wrapped around the body of your
original function:
function f(x) {
return new Promise(function(resolve, reject) {
try {
resolve((function(x) { /* body */ })(x));
}
catch(e) {
reject(e);
}
}); }