Senior-Level JavaScript Promises Interview Questions and Real-World Scenarios ππ
Introduction
Promises are a core concept in modern JavaScript, providing a way to handle asynchronous operations. Understanding how to work with Promises is essential for senior-level developers, as they are commonly used in real-world applications for handling data fetching, background tasks, error handling, and more.
In this blog, weβll cover 10 advanced interview questions about JavaScript Promises, including real-world scenarios and solutions, with a full explanation to help you master these concepts.
1. How Would You Implement Promise.all
with a Timeout?
Scenario: You want to run multiple Promises in parallel using Promise.all
, but if one takes too long (say, more than 5 seconds), you want to timeout and reject the entire operation.
Explanation: Promise.all
doesn't have a built-in timeout feature, but you can create a custom timeoutPromise
and use Promise.race
to race it against Promise.all
.
Solution Code:
function timeoutPromise(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error('Operation timed out')), ms)
);
}
function runWithTimeout(promises, timeout) {
return Promise.race([
Promise.all(promises),
timeoutPromise(timeout)
]);
}const promise1 = new Promise((resolve) => setTimeout(() => resolve('Promise 1'), 1000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve('Promise 2'), 2000));
const promise3 = new Promise((resolve) => setTimeout(() => resolve('Promise 3'), 10000)); // Causes the timeoutrunWithTimeout([promise1, promise2, promise3], 5000)
.then((results) => console.log('Results:', results))
.catch((error) => console.error('Error:', error.message));
Explanation of Solution: We create a timeoutPromise
that rejects after the specified time. The runWithTimeout
function uses Promise.race
to race between Promise.all
and timeoutPromise
, rejecting if the timeout occurs first.
2. How Do You Cancel a Promise?
Scenario: You have a long-running API request with fetch
, and you need to cancel it if the user navigates away.
Solution: Use the AbortController
API with fetch
to cancel an ongoing request.
Code Example:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://jsonplaceholder.typicode.com/posts', { signal })
.then((response) => response.json())
.then((data) => console.log('Fetched Data:', data))
.catch((error) => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});// Simulate user navigation and cancel the fetch after 1 second
setTimeout(() => controller.abort(), 1000);
3. How Would You Implement a Retry Mechanism for a Promise?
Scenario: You want to retry a Promise if it fails, with a delay between retries.
Solution Code:
function retryPromise(promiseFn, retries, delay) {
return new Promise((resolve, reject) => {
const attempt = (retriesLeft) => {
promiseFn()
.then(resolve)
.catch((error) => {
if (retriesLeft === 0) {
reject(error);
} else {
console.log(`Retrying... Attempts left: ${retriesLeft}`);
setTimeout(() => attempt(retriesLeft - 1), delay);
}
});
};
attempt(retries);
});
}
function unstableRequest() {
return new Promise((resolve, reject) => {
const success = Math.random() > 0.5;
setTimeout(() => {
success ? resolve('Request succeeded') : reject('Request failed');
}, 500);
});
}retryPromise(unstableRequest, 3, 1000)
.then((result) => console.log(result))
.catch((error) => console.error('Final failure:', error));
4. How Can You Implement a Promise Pool?
Scenario: Limit the number of concurrent Promises to avoid overloading resources.
Code Example:
function promisePool(tasks, poolLimit) {
const results = [];
const executing = [];
const enqueue = () => {
if (tasks.length === 0) {
return Promise.resolve();
} const task = tasks.shift();
const promise = task().then((result) => {
results.push(result);
executing.splice(executing.indexOf(promise), 1);
}); executing.push(promise); const r = Promise.resolve();
if (executing.length >= poolLimit) {
r = Promise.race(executing);
} return r.then(() => enqueue());
}; return enqueue().then(() => results);
}const tasks = Array.from({ length: 10 }, (_, i) => () => {
return new Promise((resolve) => setTimeout(() => resolve(`Task ${i + 1}`), 1000));
});
promisePool(tasks, 3).then((results) => console.log('All tasks completed:', results));
5. How Would You Handle Error Recovery in Promise Chaining?
Scenario: If a step in a chain fails, provide a fallback value.
Solution:
function getData() {
return new Promise((resolve, reject) => setTimeout(() => reject('Failed to fetch data'), 1000));
}
function processData(data) {
return new Promise((resolve) => setTimeout(() => resolve(`Processed ${data}`), 1000));
}function saveData(data) {
return new Promise((resolve) => setTimeout(() => resolve(`Saved ${data}`), 1000));
}getData()
.catch((error) => {
console.warn('Error fetching data:', error);
return 'Fallback data';
})
.then(processData)
.then(saveData)
.then((result) => console.log('Final result:', result))
.catch((error) => console.error('Error during processing:', error));
6. Explain the Promise.resolve()
and Promise.reject()
Methods
Scenario: These methods allow you to create resolved or rejected Promises quickly.
Code Example:
const resolvedPromise = Promise.resolve('Immediate value');
resolvedPromise.then((value) => console.log(value)); // Output: Immediate value
const rejectedPromise = Promise.reject('An error occurred');
rejectedPromise.catch((error) => console.error(error)); // Output: An error occurred
7. How Would You Use Promise.any()
in a Real-World Scenario?
Scenario: When multiple data sources can fulfill a request, return the fastest one.
Code Example:
const promise1 = new Promise((resolve) => setTimeout(() => resolve('Source 1'), 2000));
const promise2 = new Promise((resolve) => setTimeout(() => resolve('Source 2'), 1000));
const promise3 = new Promise((_, reject) => setTimeout(() => reject('Source 3 failed'), 1500));
Promise.any([promise1, promise2, promise3])
.then((result) => console.log('First resolved:', result))
.catch((error) => console.error('All promises rejected:', error));
8. How Can You Use Promise.finally()
for Cleanup Operations?
Scenario: Perform cleanup whether a Promise is fulfilled or rejected.
Code Example:
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((data) => console.log('Data:', data))
.catch((error) => console.error('Fetch failed:', error))
.finally(() => console.log('Cleanup operation completed'));
9. What is a Promise Chain Deadlock? How Can You Avoid It?
Scenario: Chaining a Promise that never settles creates a deadlock.
Solution: Avoid using unresolved Promises in chains.
Code Example:
new Promise((resolve) => resolve('Start'))
.then(() => {
return new Promise(() => {
// Intentionally not resolving or rejecting, causing a deadlock
});
})
.then(() => console.log('This will never run'))
.catch(() => console.log('This won\'t either'));
10. How Can You Implement Throttling with Promises?
Scenario: Limit the frequency of API requests.
Solution Code:
let isThrottled = false;
function throttledPromise(promiseFn, delay) {
return new Promise((resolve, reject) => {
if (isThrottled) {
reject('Request is throttled');
} else {
isThrottled = true;
promiseFn()
.then(resolve)
.catch(reject)
.finally(() => {
setTimeout(() => (isThrottled = false), delay);
});
}
});
}function apiRequest() {
return new Promise((resolve) => setTimeout(() => resolve('API response'), 500));
}throttledPromise(apiRequest, 2000)
.then((result) => console.log(result))
.catch((error) => console.error(error));
Conclusion
Understanding advanced Promise patterns is crucial for senior-level JavaScript interviews. These 10 questions cover essential concepts like timeouts, retries, Promise pools, and error handling, preparing you for challenging interview scenarios and real-world applications.
Happy Coding! π