Interview Question: Priority Queue for Promises in JavaScript

devsmitra

Rahul Sharma

Posted on November 8, 2023

Interview Question: Priority Queue for Promises in JavaScript

Implementing a Priority Queue for Promises in JavaScript

Imagine a scenario where you have a list of promises, and you want to get the result of the first resolved promise. If the first promise is rejected, you'd like to ensure that the function returns the result of the next resolved promise. And, in the worst-case scenario where all promises are rejected, you want to gather a list of errors. In this blog post, we'll discuss how to implement a priority queue for promises in JavaScript to achieve this.

The Problem

Consider the following example:



const resolveIn = (time) => new Promise((resolve) => setTimeout(() => resolve(time), time));
const rejectIn = (time) => new Promise((_, reject) => setTimeout(() => reject(time), time));

const firstResolved = async (promises) => {
  // Your solution goes here
};

const promises1 = [rejectIn(1000), resolveIn(500), resolveIn(20000)];
const result1 = await firstResolved(promises1);
console.log(result1); // 500 (after 1000ms)

const promises2 = [rejectIn(1000), resolveIn(5000), resolveIn(100)];
const result2 = await firstResolved(promises2);
console.log(result2); // 5000 (after 5000ms)


Enter fullscreen mode Exit fullscreen mode

In this example, we have an array of promises. The firstResolved function should return the first resolved promise, and if the first promise is rejected, it should return the next resolved promise. If all promises are rejected, it should return a list of errors.

Solution Overview

To implement this behavior, we need to execute all promises in parallel, similar to running a race. We cannot use Promise.all because it would reject the entire operation if any promise is rejected. Also, we don't want to wait for the last promise to resolve; we want to return the result as soon as possible. The same issue applies to Promise.allSettled.

Promise.any is a good candidate for this operation. It returns the first resolved promise but in the second example, it returns the result of the last promise because it's the first resolved promise. This is not the behavior we want.

Let's implement a custom solution to handle this priority queue for promises.

The Implementation



const firstResolved = (promises) => {
  if (!promises || !promises.length)
    return Promise.reject("No promises provided");

  const results = Array(promises.length).fill(null);

  return new Promise((resolve, reject) => {
    const checkResults = () => {
      for (let i = 0; i < results.length; i++) {
        if (results[i] === null) return;
        if (results[i].status === "resolved") return resolve(results[i].value);
      }
      reject(results);
    };

    promises.forEach((promise, i) => {
      Promise.resolve(promise).then((result) => {
          results[i] = { status: "resolved", value: result };
        }).catch((error) => {
          results[i] = { status: "rejected", reason: error };
        }).finally(checkResults);
    });
  });
};


Enter fullscreen mode Exit fullscreen mode

Here's how the solution works:

  1. We first check if any promises are provided. If not, we reject the operation with an appropriate message.

  2. We create an array called results with the same length as the number of promises. This array will store the status and value of each promise.

  3. We return a new promise that handles the resolution and rejection logic.

  4. The checkResults function is responsible for checking the status of the promises. It looks through the results array and immediately resolves the promise when it finds the first resolved promise. If all promises are rejected, it rejects the operation with the collected error information.

  5. We iterate through the promises using a forEach loop. For each promise, we attach then, catch, and finally handlers. The then handler updates the results array with the resolved value, the catch handler updates it with the rejection reason, and the finally handler calls checkResults.

With this implementation, the function will return the result of the first resolved promise or a list of errors if all promises are rejected.

Testing the Implementation

Let's test the firstResolved function with the examples we discussed earlier.



const promises1 = [rejectIn(1000), resolveIn(500), resolveIn(20000)];
const result1 = await firstResolved(promises1);
console.log(result1); // 500 (after 1000ms)

const promises2 = [rejectIn(1000), resolveIn(5000), resolveIn(100)];
const result2 = await firstResolved(promises2);
console.log(result2); // 5000 (after 5000ms)


Enter fullscreen mode Exit fullscreen mode

Thanks for reading! I hope you enjoyed this article. Feel free to share your thoughts in the comments below.


Must Read If you haven't
3 steps to create custom state management library with React and Context API
How to cancel Javascript API request with AbortController
Getting started with SolidJs – A Beginner's Guide

More content at Dev.to.
Catch me on

Youtube Github LinkedIn Medium Stackblitz Hashnode HackerNoon

💖 💪 🙅 🚩
devsmitra
Rahul Sharma

Posted on November 8, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related