
Some JavaScript promise interview questions
In this article, we’ll go over some commonly asked interview questions on JS promises.
1 What is the difference between Promise.all
and Promise.allSettled
?
Promise.all and Promise.allSettled are both used to handle multiple promises, but they differ in how they handle failures and successes.
Promise.all
Returns a single promise that resolves when all the promises in the input array resolve, or rejects when any of the promises reject. If any promise in the array rejects, the returned promise is rejected with the reason of the first promise that rejected.
Example:
Promise.all([promise1, promise2, promise3]).then((results) => {
// All promises resolved successfully
}).catch((error) => {
// Any promise rejected
});
Promise.allSettled
Returns a promise that resolves when all of the promises in the input array have either resolved or rejected. The returned promise resolves with an array of objects, each representing the outcome of the corresponding promise.
Example:
Promise.allSettled([promise1, promise2, promise3]).then((results) => {
results.forEach((result) => {
if (result.status === 'fulfilled') {
// Promise resolved successfully
} else if (result.status === 'rejected') {
// Promise rejected
}
});
});
In summary, Promise.all stops at the first rejection, while Promise.allSettled collects the outcomes of all promises, even if some reject.
2 How do you handle errors in a chain of Promises?
Use catch() blocks
Attach a catch
block to the end of your promise chain to catch any errors that may have occurred in the chain.
promise1
.then(result1 => {
// handle result1
})
.then(result2 => {
// handle result2
})
.catch(error => {
// handle error
});
Return a rejected promise
Within a catch
block, you can return a rejected promise using Promise.reject(error)
to propagate the error to the next catch
block.
promise1
.then(result1 => {
// handle result1
})
.then(result2 => {
// handle result2
})
.catch(error => {
return Promise.reject(error);
})
.catch(error => {
// handle the propagated error
});
Use try-catch blocks
Wrap your code within a try
block and use a catch
block to handle any errors that may occur.
promise1
.then(result1 => {
try {
// handle result1
} catch (error) {
// handle error
}
})
.then(result2 => {
try {
// handle result2
} catch (error) {
// handle error
}
});
Use Promise.prototype.finally()
Attach a finally
block to the end of your promise chain to execute code regardless of whether the promises resolved or rejected.
promise1
.then(result1 => {
// handle result1
})
.then(result2 => {
// handle result2
})
.finally(() => {
// execute code regardless of outcome
});
3 What is the difference between async/await
and using Promises with .then
?
Async/await and Promises with .then are two different approaches to handling asynchronous code in JavaScript. While they can achieve similar results, there are key differences between them:
Promises with .then
- Use the
then
method to handle the resolution or rejection of a promise. - Chain multiple
then
calls to handle sequential asynchronous operations. - Error handling is done using
catch
blocks. - Code can become nested and hard to read with multiple
then
calls.
Example:
promise1
.then(result1 => {
return promise2;
})
.then(result2 => {
return promise3;
})
.then(result3 => {
// handle final result
})
.catch(error => {
// handle error
});
Async/await
- Use the
async
keyword to define a function that returns a promise. - Use the
await
keyword to pause the execution of the function until a promise is resolved or rejected. - Error handling is done using
try-catch
blocks. - Code looks more synchronous and is often easier to read.
Example:
async function example() {
try {
const result1 = await promise1;
const result2 = await promise2;
const result3 = await promise3;
// handle final result
} catch (error) {
// handle error
}
}
Key differences
- Readability: Async/await code looks more synchronous and is often easier to read, while Promise chains can become nested and hard to read.
- Error handling: Async/await uses
try-catch
blocks for error handling, while Promises usecatch
blocks. - Code structure: Async/await code is more linear, while Promise chains can be more flexible.
4 How do you cancel a Promise?
Canceling a Promise is a bit tricky, as Promises are designed to be immutable and once created, they cannot be canceled or changed. However, there are ways to achieve cancellation-like behavior:
Use a cancel token
Create a cancel token using a library like cancel-token
or axios
(which has built-in cancel token support). Pass the token to the promise and check for cancellation within the promise. If canceled, reject the promise.
const cancelToken = new CancelToken();
const promise = new Promise((resolve, reject) => {
// Check for cancellation
if (cancelToken.isCancelled) {
reject(new CancelError());
}
// Perform async operation
});
Use a wrapper promise
Create a wrapper promise that can be canceled. When the wrapper promise is canceled, it rejects the original promise.
function cancelablePromise(promise) {
let isCancelled = false;
const wrapperPromise = new Promise((resolve, reject) => {
promise.then((result) => {
if (!isCancelled) {
resolve(result);
}
}).catch((error) => {
if (!isCancelled) {
reject(error);
}
});
});
return {
promise: wrapperPromise,
cancel() {
isCancelled = true;
},
};
}
Use async/await with a timeout
Use async/await
with a timeout to achieve a cancellation-like effect. If the promise doesn't resolve within the timeout, reject it.
async function example() {
try {
const result = await promise.timeout(5000); // 5 seconds
} catch (error) {
// Handle timeout or rejection
}
}
Note that these approaches don’t actually cancel the underlying async operation, but rather provide a way to handle cancellation-like behavior in your code.
5 What is the difference between a resolved Promise and a fulfilled Promise?
Resolved and fulfilled are two related but distinct concepts in the context of Promises:
Resolved
A promise is considered resolved when it has either been fulfilled (successfully resolved) or rejected (unsuccessfully resolved). In other words, a resolved promise has reached its final state, and its outcome is known.
Fulfilled
A promise is considered fulfilled when it has successfully resolved and returned a value. This means that the promise has completed its async operation and has a successful result.
In summary:
- Resolved refers to the final state of a promise, whether it’s fulfilled or rejected.
- Fulfilled specifically refers to a successfully resolved promise with a value.
To illustrate the difference:
- A promise that resolves with a value is both resolved and fulfilled.
- A promise that rejects with an error is resolved but not fulfilled.
Here’s a code example to demonstrate this:
const promise = new Promise((resolve, reject) => {
// Async operation
setTimeout(() => {
resolve("Success!"); // Fulfilled
}, 1000);
});
promise.then((value) => {
console.log(`Promise is resolved and fulfilled with value: ${value}`);
}).catch((error) => {
console.log(`Promise is resolved but rejected with error: ${error}`);
});
In this example, the promise is both resolved and fulfilled when it successfully resolves with the value “Success!”.
6 How do you handle multiple errors in a chain of Promises?
Use catch blocks
Attach a catch
block to each promise in the chain to handle errors specific to that promise.
promise1
.then(result1 => {
// handle result1
})
.catch(error1 => {
// handle error1
})
.then(result2 => {
// handle result2
})
.catch(error2 => {
// handle error2
});
Return a rejected promise
Within a catch
block, return a rejected promise using Promise.reject(error)
to propagate the error to the next catch
block.
promise1
.then(result1 => {
// handle result1
})
.catch(error1 => {
return Promise.reject(error1);
})
.then(result2 => {
// handle result2
})
.catch(error2 => {
// handle error2
});
Use a global error handler
Define a global error handler using window.onerror
or a similar mechanism to catch any unhandled errors.
window.onerror = (error) => {
// Handle global error
};
Use a promise library with error handling
Utilize a promise library like Bluebird or Q, which provide built-in error handling mechanisms, such as catch
and finally
blocks.
const Promise = require('bluebird');
promise1
.then(result1 => {
// handle result1
})
.catch(error1 => {
// handle error1
})
.finally(() => {
// execute code regardless of outcome
});
Use async/await with try-catch
Write your code using async/await and wrap it in a try-catch block to handle errors.
async function example() {
try {
const result1 = await promise1;
const result2 = await promise2;
} catch (error) {
// handle error
}
}
7 What is the purpose of Promise.prototype.finally
?
Promise.prototype.finally is a method that allows you to execute code regardless of whether a promise is fulfilled or rejected. It’s called when a promise is either resolved or rejected, and it’s used to perform cleanup or other tasks that need to happen in either case.
The purpose of finally is to:
- Execute code regardless of outcome: Run code whether the promise is fulfilled or rejected.
- Cleanup resources: Release resources, close connections, or perform other cleanup tasks.
- Log or track errors: Log errors or track them for analytics purposes.
- Provide a final chance to handle errors: Make a last attempt to handle errors or recover from them.
- Ensure code execution: Guarantee that certain code is executed, even if an error occurs.
Here’s an example:
promise
.then(result => {
// handle result
})
.catch(error => {
// handle error
})
.finally(() => {
// execute code regardless of outcome
console.log("Promise settled");
});
In this example, the finally block will be executed whether the promise is fulfilled or rejected, and it will log “Promise settled” to the console.
Note: finally is not a substitute for proper error handling. It’s meant to complement error handling, not replace it.
8 Can you explain the concept of “Promise hell” and how to avoid it?
Promise hell, also known as callback hell, refers to the unreadable and unmaintainable nature of deeply nested callbacks in JavaScript, making code difficult to understand and maintain. It occurs when multiple asynchronous operations are chained together, resulting in a complex and hard-to-read code structure.
Characteristics of Promise hell
- Deeply nested callbacks or promises
- Unreadable and hard-to-understand code
- Difficult to maintain and debug
- Error handling becomes complicated
How to avoid Promise hell
- Use async/await: Write asynchronous code that looks synchronous, making it easier to read and maintain.
- Break up long chains: Split long promise chains into smaller, more manageable functions.
- Use promise libraries: Utilize libraries like Bluebird or Q, which provide features like promise chaining and error handling.
- Write modular code: Divide code into smaller, independent modules, each handling a specific task.
- Use clear and concise naming: Choose descriptive variable and function names to improve code readability.
- Use comments and documentation: Explain the purpose and functionality of your code to help others understand it.
- Avoid nesting: Try to avoid deeply nested callbacks or promises by using alternative approaches like async/await or promise chaining.
- Use error handling mechanisms: Implement proper error handling using try-catch blocks, catch blocks, or error-handling libraries.
Here is an example:
fetchData()
.then(data => {
return fetchMoreData(data)
.then(moreData => {
return fetchEvenMoreData(moreData)
.then(evenMoreData => {
return processAllTheData(data, moreData, evenMoreData)
.then(processedData => {
console.log(processedData);
})
.catch(error => {
console.error(error);
});
})
.catch(error => {
console.error(error);
});
})
.catch(error => {
console.error(error);
});
})
.catch(error => {
console.error(error);
});
This code demonstrates the characteristics of Promise hell:
- Deeply nested callbacks (four levels of nesting)
- Unreadable and hard-to-understand code
- Difficult to maintain and debug
- Error handling becomes complicated
9 How do you create a Promise that resolves after a certain amount of time?
You can create a Promise that resolves after a certain amount of time using the setTimeout
function and the Promise
constructor. Here's an example:
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
This function takes a time in milliseconds (ms) as an argument and returns a Promise that resolves after that amount of time. You can use it like this:
delay(2000).then(() => {
console.log("Resolved after 2 seconds");
});
This will log “Resolved after 2 seconds” to the console after a 2-second delay.
Alternatively, you can use the setTimeout
function directly with a Promise:
const promise = new Promise(resolve => {
setTimeout(resolve, 2000);
});
promise.then(() => {
console.log("Resolved after 2 seconds");
});
Both of these approaches create a Promise that resolves after a certain amount of time, allowing you to handle asynchronous operations with ease.
Note: You can also use Promise.resolve()
and setTimeout
to create a promise that resolves after a certain amount of time:
const promise = Promise.resolve().then(() => {
return new Promise(resolve => {
setTimeout(resolve, 2000);
});
});
promise.then(() => {
console.log("Resolved after 2 seconds");
});
10 Can you write a function that takes a Promise as an input and returns a new Promise that resolves with the same value, but after a certain delay?
Here’s a function that takes a Promise and a delay time (in milliseconds) as inputs and returns a new Promise that resolves with the same value, but after the specified delay:
function delayPromise(promise, delay) {
return new Promise((resolve) => {
promise.then((value) => {
setTimeout(() => {
resolve(value);
}, delay);
});
});
}
Here’s how it works:
- Create a new Promise using the
Promise
constructor. - Use the
then
method to attach a callback to the input Promise. - In the callback, use
setTimeout
to schedule a function to be executed after the specified delay. - In the scheduled function, resolve the new Promise with the same value as the original Promise.
You can use this function like this:
const originalPromise = fetch('https://example.com/data');
const delayedPromise = delayPromise(originalPromise, 2000); // Delay by 2 seconds
delayedPromise.then((data) => {
console.log(data); // Data will be logged after 2 seconds
});
Note that this function preserves the original Promise’s behavior, including any error handling. If the original Promise rejects, the new Promise will also reject with the same error.
Thanks for reading!