Search code examples
javascripttypescriptes6-promise

How to call an API twice if there is an error occurred?


I have an internal API that I would like to post data. Depends on some cases, I am seeing errors. So what I would like to do is to call it again if there is an error occurred.

What I did was to create a counter to pass it to the function and call the function recursively as below. This gives me the error as below:

UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

Here is how I call the function:

....
private RETRY_API = 1;
....

 try {
    await this.callAPI(request, this.RETRY_API);
} catch (error) {
    console.log('error', error);
}

This program never comes to the catch block above.

And here is my actual function that I call the API:

private async callAPI(request, retry) {
    return new Promise((resolve, reject) => {
       someService.postApiRequest('api/url', request, async(err: any, httpCode: number, data) => {
     if (this.RETRY_API == 2) {
         return reject(err);
      } else if (err) {
          this.callAPI(request, retry);
          this.RETRY_API++;
      } else if ( httpCode !== 200 ) {
          this.RETRY_API = 2;
          // some stuff
      } else {
           this.RETRY_API = 2;
           // some stuff
           return resolve(data);
      }
   });
  })
}

Not sure what I am missing. If there is a better way to call the API twice if an error occurred, that would be great if you let me know.


Solution

  • Let's organize a little differently. First, a promise-wrapper for the api...

    private async callAPI(request) {
      return new Promise((resolve, reject) => {
        someService.postApiRequest('api/url', request,(err: any, httpCode: number, data) => {
          err ? reject(err) : resolve(data);
        });
      });
    }
    

    A utility function to use setTimeout with a promise...

    async function delay(t) {
      return new Promise(resolve => setTimeout(resolve, t));
    }
      
    

    Now, a function that calls and retries with delay...

    private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
      try {
        return await callAPI(request);
      } catch (error) {
        if (retryCount <= 0) throw err;
        await delay(retryDelay);
        return callAPIWithRetry(request, retryCount-1, retryDelay);
      }
    }
    

    If you can't force a failure on the api to test the error path some other way, you can at least try this...

    private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
      try {
        // I hate to do this, but the only way I can test the error path is to change the code here to throw an error
        // return await callAPI(request);
        await delay(500);
        throw("mock error");
      } catch (error) {
        if (retryCount <= 0) throw err;
        await delay(retryDelay);
        return callAPIWithRetry(request, retryCount-1, retryDelay);
      }
    }