In TypeScript, what is the method to specifically retrieve only resolved Promises while disregarding errors?

I have come up with a function that is supposed to take an array of Promises and only return the resolved ones while ignoring any errors. It's similar to Promise.all but without considering errors. The challenge is implementing this function correctly in TypeScript. Previously, when I was using just JavaScript, I expected the desired results.

function promiseIgnoreErrors<T> (arrayOfPromise: Array<T>)  {
    const isArrayEmpty =
      Array.isArray(arrayOfPromise) && arrayOfPromise.length === 0;
  
    return new Promise((resolve, reject) => {
      if (isArrayEmpty) reject(new Error('Forever pending array'));
  
      let resolvedArray: Array<T> = []
      
      let resolvedCounter: number = 0;
  
      arrayOfPromise.forEach((promise, index) => {
        if (promise instanceof Promise) {
          promise
            .then((data) => {
              resolvedArray[index] = data
              resolvedCounter++;      
              if (arrayOfPromise.length === resolvedCounter) resolve(resolvedArray)   
            })
            .catch(() => {
              resolvedCounter++;
              if (arrayOfPromise.length === resolvedCounter) resolve(resolvedArray)
            })
        } else {
          resolvedArray[index] = promise
          resolvedCounter++;
          if (arrayOfPromise.length === resolvedCounter) resolve(resolvedArray)   
        }
      })
    });
  };

Here is a brief overview of my problem along with some sample variables:

const p1 = new Promise((resolve) => setTimeout(() => resolve('JS'), 500))
const p2 = 22
const p3 = new Promise((reject) => setTimeout(() => reject(new Error('Oops')), 100))
const p4 = new Promise((resolve) => setTimeout(() => resolve('TS'), 200))

When I pass these variables as an array,

promiseIgnoreErrors([p1, p2, p3, Promise.reject(10), p4])

I expect to get:

Promise {<pending>}
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(3)
// ['JS', 22, 'TS']

However, the actual output is:

Promise {<pending>}
    [[PromiseState]]: "fulfilled"
    [[PromiseResult]]: Array(5)
    // ['JS', 22, Error: 'Oops, ?, 'TS']

You can see this issue in action here: Playground

I suspect the issue lies with the generic type and specifically this line:

let resolvedArray: Array<T> = []

because it suggests that the returned array must consist of the same elements as the input. Maybe it should be something like:

let resolvedArray: Array<K> extends T = []

While I know the above line is incorrect, maybe following this idea could lead to a solution.. Or perhaps there's another flaw in my function?

I would appreciate any help!

Answer №1

This appears to be sufficient.

The concept here is that ERRORED serves as a sentinel object; if a promise within the array throws an error, it is caught and transformed into an ERRORED.

isNotErrored(x) is a type guard function that assures TypeScript that x is not the sentinel, resulting in the type of results.filter(isNotErrored) being T[], rather than (T | '__ERRORED__')[].

const ERRORED = '__ERRORED__';
type ErroredType = typeof ERRORED;

function isNotErrored<T>(x: T | ErroredType): x is T {
  return x !== ERRORED;
}

async function promiseIgnoreErrors<T>(promises: Array<Promise<T>>): Promise<Array<T>> {
  const results = await Promise.all(promises.map(p => p.catch(() => ERRORED as ErroredType)));
  return results.filter(isNotErrored);
}

For example:


promiseIgnoreErrors([Promise.resolve(8), Promise.reject("oh no"), Promise.resolve(16)]).then(r => {
  console.log(r);
});

will log:

[8, 16]

ignoring any rejections.

EDIT:

In the case of the original inputs:

const p1 = new Promise((resolve) => setTimeout(() => resolve('JS'), 500))
const p2 = 22
const p3 = new Promise((reject) => setTimeout(() => reject(new Error('Oops')), 100))
const p4 = new Promise((resolve) => setTimeout(() => resolve('TS'), 200))

this will not pass type-checking, as not all inputs are promises, which is the intended behavior. As mentioned in the comments, plain values should be wrapped with Promise.resolve(), for instance:

const p2 = Promise.resolve(22)

Alternatively, you could handle this internally:


function isPromiseLike<T>(x: T | Promise<T>): x is Promise<T> {
  return (x as any).then;  // Checks whether the object has a `then`; usually sufficient.
}

async function promiseIgnoreErrors2<T>(values: Array<T | Promise<T>>): Promise<Array<T>> {
  const promises = values.map((v) => isPromiseLike(v) ? v : Promise.resolve(v));
  const results = await Promise.all(promises.map(p => p.catch(() => ERRORED as ErroredType)));
  return results.filter(isNotErrored);
}

Answer №2

async function handlePromises<T>(arrayOfPromise: Array<T>) {
  const promises = await Promise.all(arrayOfPromise.map((p) => convertToPromise(p)))
  return promises.filter((v) => v != null)
}

const convertToPromise = <T>(value: T) =>
  Promise.resolve(value)
    .then((v) => (v instanceof Error ? null : v))
    .catch(() => null)


const result = handlePromises([promise1, promise2, promise3, promise4, promise5])

console.log(result)                // Promise { <pending> }
result.then((x) => console.log(x)) // [ 'JS', 22, 'TS' ]

Update

Removed flatMap version to prevent confusion

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Steps for selectively targeting and updating a group of properties in a TypeScript class

Is there a way to consolidate this code into one function that can handle all the tasks below? I'm adding more text here to meet the requirements and hoping for a solution. Thank you! TypeScript is an amazing language that differs slightly from JavaS ...

Dealing with extended render times in React applications

Currently, I'm working with a limited set of 100 documents per page and utilizing a wrapper component for certain conditional actions. const onClickCheckbox = (order: OrderProps) => { const _ordersToExport = [...ordersToExport]; const ind ...

Is there a way to retrieve the name of a JSON or obtain the file path using NodeJS Express Router?

I've been experimenting with adding a named routers feature to the Express router for my NodeJS project. I managed to implement it successfully, but there's one thing I'm stuck on... the path. Specifically, when I have a route setup like thi ...

Troubleshooting problem in Grunt-TS, Grunt, watch/compile, and Dropbox integration

UPDATE: Recently, I've been experiencing some issues with another computer when it comes to watching and compiling. It seems like the tscommandxxxxxx.tmp.txt files that are generated during compilation might be causing the trouble... sometimes they ar ...

Leveraging configuration files for HTML pages without any server-side code

I am currently working on developing an app using phonegap. At the moment, I have the reference to a script in every page like this: <script language="javascript" src="http://someServer/js/myscript.js"></script> If the server 'someServer ...

Leveraging Toaster Notifications in AngularJS for Custom Exception Management and Logging

Implemented the use of AngularJS Toaster for effective notification handling. To customize exception handling, set up in index.html as shown below <toaster-container toaster-options="{'time-out': 3000, 'position-class': 'toast ...

The process of altering the color of a table row in JavaScript can be done after dismissing a pop-up that was triggered by a button within the same row

I am tasked with changing the color of a specific table row based on user interaction. The table consists of multiple rows, each containing a button or image. When the user clicks on one of these buttons or images, a popup appears. Upon successful data sub ...

Utilizing React Bootstrap with TypeScript for Styling Active NavItem with Inline CSS

Is it possible to change the background color of the active NavItem element to green using inline CSS in React Bootstrap and React Router Dom? I am currently using TypeScript 2.2 and React. If not, should I create a CSS class instead? Here is the code sni ...

Angular: Enhance communication with controllers and directives

Currently, I am in the process of learning Angular. One interesting feature I have been working on is a directive that displays a select element. Whenever there is a change in this select element due to the ng-change event, a specific method defined in the ...

Utilizing callback functions within an AJAX request to return a response and trigger further functions within a promise chain

While working on a React project, I encountered three different scenarios regarding making AJAX requests using axios and redux-promise. Can anyone provide some insight into the differences in these cases? Case 1 (the payload is undefined - why?): axios.g ...

Transforming a request from Angular to PHP through formatting

I am currently working on creating an add function for my Angular application that communicates with my PHP back-end. I am attempting to send data to the server using a transformationRequest, but I am unsure about the correct format that matches the $_POST ...

Is there a way to determine the number of clicks on something?

I'm attempting to track the number of times a click event occurs. What is the best method to achieve this? There are two elements present on the page and I need to monitor clicks on both of them. The pseudo-code I have in mind looks something like ...

Using a combination of ajax and php to enhance the voting system

Thank you for taking the time to read this. I am currently working on improving my skills, so I decided to embark on a project to enhance my knowledge. I have created a basic voting system where each content is displayed using PHP and includes an up or do ...

Why is DynamoDB still not deleting the item even though the promise returns successfully?

Using the DynamoDB DocumentClient, I attempted to delete items across multiple tables using Class: AWS.DynamoDB.DocumentClient A problem arose when I tried to delete items from multiple tables using promised.all(). The operation ran without deleting the i ...

Encountered a error 500 while attempting to POST using Postman in Node.js

After successfully setting up the server, I encountered an issue when attempting to create a user using Postman with the details in Application/JSON format. The server responded with: POST /sign-up 500. Initially, I tried specifying utf-8 without succes ...

Utilizing asynchronous false in Wordpress Ajax functionality

Is there a way to use the AJAX return value outside the function in WordPress? For example: function get_login_member($) { $.post(ajax_object.ajax_url, {action: 'getloginmember'}, function (data) { data = JSON.parse(data); ...

Vue component input form not providing expected result

Here is the code snippet for my "ecedata" component with an input field: <template> <div class="col-l-4"> <p style="text-align: center">Data/day (GB)</p> <div class="form-input-set" style="background: white"& ...

Node app experiencing issues with passport authentication request route only in production mode

Currently, I am facing an issue with my MERN app that includes passport for authentication flow. It runs smoothly in development mode but encounters major problems in production mode that I am struggling to resolve. The bug seems elusive and I can't p ...

Access various data from the local storage and display them inside a div container

I am trying to display the keys and their values for all the data stored in the local storage. Here is the HTML and JavaScript code I have written: // Setting some values in the local storage localStorage.setItem("lastname", "Smith"); localStorage. ...

How can we use the useState hook in React to dynamically generate state variables?

I'm currently working on a React app where input fields need to be stored in the state. While I can use the useState hook to easily store these values, the challenge I'm facing is that I don't know what fields are needed since they are retri ...