Promises are used to get the result from an asynchronous code execution in JavaScript. In a web application, Promises are used almost everywhere. Every data that flows in and out of the application to a remote server is an asynchronous call.
Promise()
constructorThe Promise()
constructor takes an executor function which receives two parameters. They are resolveFunc
and rejectFunc
.
const timer = async (timeoutInMilliSeconds: number): Promise<string> => {
return new Promise((resolve, reject) => {
if (timeoutInMilliSeconds < 0) {
reject('Timer stopped as timeoutInMilliSeconds is negative');
} else {
setTimeout(() => {
resolve('Timer successfully completed');
}, timeoutInMilliSeconds);
}
});
};
then
and catch
const timerWithoutAsync = (timeoutInMilliSeconds: number): void => {
console.log('Timer started');
timer(timeoutInMilliSeconds)
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
console.log('Timer Finished');
};
If timeoutInMilliSeconds
is positive, setTimeout
is executed. Callback provided to setTimeout
is executed when the timeoutInMilliSeconds
is elapsed.
The callback executes the resolve
function which returns a string to timerWithoutAsync
.
The return value is passed to the then
block callback which logs the string to the console.
If timeoutInMilliSeconds
is negative, reject
function is executed which returns a string to timerWithoutAsync
.
The return value from reject
is considered similar to an error and hence it is passed to the catch
block callback which logs the error to the console.
async
and await
const timerWithAsync = async (timeoutInMilliSeconds: number): Promise<void> => {
console.log('Timer started');
try {
const result = await timer(timeoutInMilliSeconds);
console.log(result);
} catch (error) {
console.log(error);
}
console.log('Timer Finished');
};
If timeoutInMilliSeconds
is positive, setTimeout
is executed. Callback provided to setTimeout
is executed when the timeoutInMilliSeconds
is elapsed.
The callback executes the resolve
function which returns a string to timerWithAsync
and is stored inside the variable result
. The result
is logged into the console.
If timeoutInMilliSeconds
is negative, the reject
function is executed which returns a string to timerWithAsync
.
The return value from reject
is considered similar to an error and hence it is caught by the try-catch
block which logs the error to the console.
then-catch
vs async-await
The key difference between using then-catch
and async-await
is the order in which the consecutive code is executed.
By using then-catch
, the code execution continues without waiting for the promise to be fulfilled. Let us run timerWithoutAsync(5000)
and see the console statements.
By using async-await
, the code execution is stopped and waits for the promise to be fulfilled. Let us run timerWithAsync(5000)
and see the console statements.
Both are used often in the code depending on the use cases. If you can want to stop the execution until the promise is complete then use async-await
syntax otherwise you can use then-catch
syntax.
Promises can be executed sequentially when the next promise is dependent on the previous promise result.
const executeTimerSequentially = async (): Promise<void> => {
const result1 = await timer(1000);
console.log(result1);
const result2 = await timer(2000);
console.log(result2);
const result3 = await timer(3000);
console.log(result3);
};
timer(1000)
is executed.result1
is logged.timer(2000)
is executed.result2
is logged.timer(3000)
is executed.result3
is logged.The total time to complete executeTimerSequentially
is 1+2+3 = 6 seconds.
Similar results can be achieved using then()
block using promise chaining.
const executeTimerSequentially = async (): Promise<void> => {
timer(1000)
.then((result1) => {
console.log(result1);
return timer(2000);
})
.then((result2) => {
console.log(result2);
return timer(3000);
})
.then((result3) => {
console.log(result3);
});
};
Promises can be executed parallelly when the next promise is independent of the previous promise result.
const executeTimerParallely = async (): Promise<void> => {
const promise1 = timer(1000);
const promise2 = timer(2000);
const promise3 = timer(3000);
const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3]);
console.log(result1);
console.log(result2);
console.log(result3);
};
timer(1000)
, timer(2000)
, timer(3000)
will start the execution parallelly.
Promise.all()
takes in an array of promises and returns the result the all the promises only if all the promises are resolved otherwise it will be rejected. Since we are using await
, It will wait for all the promises to resolve before logging the results.
The total time to complete executeTimerParallely is max(1, 2, 3) = 3 seconds.
Promise.all()
Assume we have an array of user IDs in userIds
and we have an API function getUserDetails
which gets the user details for the given user ID. Using map
array arrow function, we get an array of promises to fetch the user details for each user ID.
We are using await
and Promise.all()
to make sure all the user details are fetched before logging the list of users.
const userIds = ['001', '002', '003'];
const getUsers = async (): Promise<void> => {
const users = await Promise.all(
userIds.map((userId) => {
getUserDetails(userId);
})
);
console.log(users);
};