Senior-Level JavaScript Promises Interview Questions and Real-World Scenarios πŸš€πŸ”

The Expert Developer
6 min readOct 26, 2024

--

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 timeout
runWithTimeout([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! πŸš€

--

--

The Expert Developer

React Native, React Js, Android, IOS, Firebase, Javascript etc