Implementing error wrapper in typescript to handle failing promises with n retries

I have a wrapper function that calls an async function:

function fetchAPI(arg1: number, arg2: number, arg3: string) {
  return new Promise((resolve, reject) => {
    try {
      myFetchFunction(arg1, arg2, arg3).then((r: any) => {
        if (!r) {
          reject("Error 01")
        }
        resolve(r)
      })
    } catch {
      reject("Error 01")
    }
  })
}

Due to the instability of the API being queried, the fetchAPI function frequently rejects.

To address this issue, I created another function that iterates over the fetchAPI function up to 3 times (with a 1-second wait interval between attempts). If all attempts fail, it resolves with undefined. If any attempt succeeds, it simply resolves with the correct response.

function fetchAPIRepeat(arg1: number, arg2: number, arg3: string, n: number = 0) {
  return new Promise((resolve) => {
    fetchAPI(arg1, arg2, arg3).then((r) => {
      resolve(r)
    }).catch(() => {
      if(n < 3) {
        console.log("Partial error: " + n)
        sleep(1000).then(() => {
          resolve(fetchAPIRepeat(arg1, arg2, arg3, n + 1))
        })
      } else {
        resolve(undefined)
      }
    })
  })
}

This solution works well for me. However, I would like to create a more generic wrapper that functions similarly to the fetchAPIRepeat but can be used with any promise-based function.

Although I attempted to create such a 'general wrapper', it is not functioning as expected:

function promiseRepeatWrapper(fn: (...args: any[]) => Promise<any>, n: number = 0, ...args: any[]) {
  return new Promise((resolve) => {
    fn(args).then((r) => {
      resolve(r)
    }).catch(() => {
      if(n < 3) {
        console.log("Partial error: " + n)
        sleep(1000).then(() => {
          resolve(this.errorWrapper(fn, n + 1, args))
        })
      } else {
        resolve(undefined)
      }
    })
  })
}

The current issue is that the console logs 'Partial error' three times and then resolves with undefined, even when the promise successfully completes all three attempts:

Partial error: 0
Partial error: 1
Partial error: 2

In addition to fixing the functionality of the function, I am open to suggestions on improving the error-handling design for my promise-repeat pattern.

Thank you.

Answer №1

If you want to implement asynchronous functionality using async/await, there is no need to pass arguments repeatedly. A more generic approach allows you to specify the number of retry attempts and the delay between retries.

Check out this typescript playground

const waitFor = (timeout: number): Promise<void> => {
    return new Promise((resolve) => {
        setTimeout(() => resolve(), timeout);
    });
}

async function retryPromise<T>(fn: () => Promise<T>, repeat = 3, retryAfter = 1000): Promise<T> {
    let currentAttempt = 1;
    while(currentAttempt <= 3) {
        try {
            return await fn();
        } catch (e) {
            currentAttempt += 1;
            await waitFor(retryAfter);
        }
    }

    // It failed after 3 retries
    throw new Error("Cannot perform this operation");
}

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

The custom native date adapter is facing compatibility issues following the upgrade of Angular/Material from version 5 to 6

In my Angular 5 application, I had implemented a custom date adapter as follows: import {NativeDateAdapter} from "@angular/material"; import {Injectable} from "@angular/core"; @Injectable() export class CustomDateAdapter extends NativeDateAdapter { ...

Shared validation between two input fields in Angular 2+

I have a unique task at hand. I am working on creating an input field with shared validation. The goal is to ensure that both fields are technically required, but if a user fills in their email address, then both fields become valid. Similarly, if they ent ...

What causes error TS2339 to occur: The property 'classList' is not found on type 'never'

Currently, I am working on creating a basic component called "BackToTop" const BackToTop: React.FC = () => { const bttEl = useRef(null); function scrollHandler(): void { var bttHtmlEl: HTMLElement | null = bttEl.current; if (bttH ...

Tips for utilizing functions in an inline HTML translation pipe

My objective is to streamline the code by using the Angular translate pipe. Currently, I find myself using 8 curly brackets and repeating the word "translate" twice... there must be a more efficient approach. Here is my current code setup: <s ...

Understanding Mongodb: the process of populating a schema that is referenced within another schema using an API

Looking to make adjustments to my Api in order to populate a referenced schema. Here's the schema I am working with: export const taskSchema = new Schema ({ user:{ type: String, required: true }, project: { type ...

What is the implication when Typescript indicates that there is no overlap between the types 'a' and 'b'?

let choice = Math.random() < 0.5 ? "a" : "b"; if (choice !== "a") { // ... } else if (choice === "b") { This situation will always be false because the values 'a' and 'b' are completely disti ...

What is the reason behind Typescript's discomfort with utilizing a basic object as an interface containing exclusively optional properties?

Trying to create a mock for uirouter's StateService has been a bit challenging for me. This is what I have attempted: beforeEach(() => { stateService = jasmine.createSpyObj('StateService', ['go']) as StateService; } ... it(& ...

How to dynamically insert variables into a separate HTML file while creating a VS Code extension

Currently working on a vscode extension, I'm facing an issue with containing the html in a string (like this: https://github.com/microsoft/vscode-extension-samples/blob/main/webview-view-sample/src/extension.ts). It leads to a large file size and lack ...

Adjusting table to include hashed passwords for migration

How can I convert a string password into a hash during migration? I've tried using the following code, but it seems the transaction completes after the selection: const users = await queryRunner.query('SELECT * FROM app_user;'); user ...

Issues have arisen with compiling TypeScript due to the absence of the 'tsc command not found' error

Attempting to set up TypeScript, I ran the command: npm install -g typescript After running tsc --version, it returned 'tsc command not found'. Despite trying various solutions from Stack Overflow, GitHub, and other sources, I am unable to reso ...

Error encountered with next-auth and the getServerSession function

Whenever I try to use the getServerSesssion function with all the necessary parameters, it results in a type error. In my code, I have the getServerAuthSession function defined as shown below: import { authOptions } from '@/pages/api/auth/[...nextauth ...

Angular dependency issue: Expected '{' or ';' for @types/node

I encountered an error while running "ng serve" in my Angular application. Originally built as Angular 2, it was upgraded to Angular 8 (with attempts at versions 6 and 7 along the way). However, after migrating from Angular 5, I started experiencing errors ...

"What is the appropriate TypeScript type for this specific JSX code - React.ReactElement, React.ReactNode, and React.FunctionComponent all prove to be inadequate in this

Having an issue with assigning a type to my prop SubmissionComponent. This prop is expecting JSX, possibly a button or a more complex structure. For example: const SubmissionComponent = <p>hi</p>; which is then used like this: <Submitter ...

Exploring the possibilities of utilizing JavaScript within TypeScript

My dynamic javascript object holds all the resources (translation strings) for my app. Here's how it is structured: var ResourceManager = (function () { function ResourceManager() { var currentLanguage = $('#activeLanguage').htm ...

A step-by-step guide to integrating a legend on a leaflet map using Angular and the ngx-leaflet plugin

I am attempting to integrate a legend into a map generated using Asymmetrik/ngx-leaflet. The tutorial I followed for creating the map can be found at https://github.com/Asymmetrik/ngx-leaflet. There are two distinct layers on the map, each requiring its ow ...

What is the process for defining a collection of strings as a data type?

Is there a way to define a strict type for a group of strings that users must adhere to? I want to restrict input to only be one of the predefined strings in the group. How can I achieve this? This is my current approach: const validTypes: Array<strin ...

TypeScript excels in typechecking when using two individual assignments, but may encounter issues when attempting typechecking with tuple

I am quite puzzled by a discovery I made and I am seeking to understand why TypeScript is displaying this behavior and what the underlying reason may be. Here is the code snippet: class A { constructor(public name : String, public x = 0, public y = 0) ...

"Upon studying the Reflect-metadata index.d.ts file, I find myself perplexed by the variances in

While utilizing the reflect-metadata package, I encountered this particular type. However, I am uncertain about its meaning and function signature. function metadata(metadataKey: any, metadataValue: any): { (target: Function): void; ( ...

Turn off the interconnected route while utilizing npm to display the tree of dependencies

If I want to display the project's dependencies tree using npm, I would use the following command: npm ls lodash The output will look something like this: > npm ls lodash npm info using [email protected] npm info using [email protected] ...

Angular definitely typed does not convert into JavaScript

After installing TypeScript on my VS2013, I obtained the Angular 1.5 Definitely Typed from the NuGet package manager. Although angular.d.ts and its components do not generate angular.js file, when I create another TypeScript file like file1.ts, the file1. ...