Tips for handling a recursive function called within an asynchronous event handler that is enclosed in a Promise.all block

Is there a way to upload multiple file chunks simultaneously and retry each failed chunk five times? I have created an example code in TypeScript Playground that demonstrates this behavior. The code excludes the use of XMLHttpRequest for simplicity.

Here is the sample code:

class FileUploader {
    onSuccess: ((this: FileUploader) => void) | null;
    onFail: ((this: FileUploader) => void) | null;

    constructor() {
        this.onSuccess = null;
        this.onFail = null;
    }

    run() {
        // Simulation of request failure or success
        this.onFail?.();
    }
}

async function uploadFile(name: string, retryCount = 0) {
    return new Promise<void>(async (resolve, reject) => {
        const runner = new FileUploader();
        runner.onSuccess = async () => {
            await new Promise((res) => setTimeout(res, 500));
            resolve();
        }
        runner.onFail = async () => {
            if (retryCount < 5) {
                console.log(`Ran ${name}, ${retryCount}`);
                const newDelay = Math.pow(2, retryCount) * 100;
                await new Promise((res) => setTimeout(res, newDelay));
                await uploadFile(name, retryCount + 1);
            }
            console.log("rejected", name);
            reject("error");
        }
        runner.run();
    });
}

async function main() {
    try {
        await Promise.all([uploadFile("file1"), uploadFile("file2"), uploadFile("file3")])
        console.log("success")
    }
    catch (ex) {
        console.log("error")
        console.log(ex);
    }
}

main();

I am looking to handle errors caught in the main function. Any suggestions on how to achieve this?

Answer №1

It's recommended to avoid using the Promise constructor due to its complexity. Refer to Is creating a new promise with a async function call bad practice? for more information on this topic. One common problem is failing to reject the promise in case of errors, which can lead to issues when handling rejections from inner functions like uploadFile().

await uploadFile(name, retryCount + 1);

If an error occurs in the innermost uploadFile() call, it will throw an error within the Promise constructor without actually rejecting the promise. To ensure proper rejection handling, you can use the catch() method to intercept any rejections from uploadFile() and invoke the reject function from the Promise constructor:

await uploadFile(name, retryCount + 1).catch(reject); 

With this adjustment, all rejections will propagate up, culminating in the rejection of Promise.all():

[LOG]: "rejected",  "file1"  // 5 
[LOG]: "rejected",  "file1"  // 4
[LOG]: "rejected",  "file1"  // 3
[LOG]: "rejected",  "file1"  // 2
[LOG]: "rejected",  "file1"  // 1
[LOG]: "rejected",  "file1"  // 0
[LOG]: "error message", "error" 

This addresses the original question.


Remember that dealing with promises inside new Promise() should be approached cautiously, as error handling can easily go awry. While this discussion goes beyond the initial query, it's important to consider these complexities.

Link to code Playground

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

Although the cucumber tests indicate success, protractor fails to interact with the elements on the webpage

Recently, I delved into the realm of Protractor+Cucumber+Typescript and devised a sample framework utilizing Page Object Design along with a small script to execute some click actions. URL: My endeavor to click on the "Customer Login" button seems futile ...

Having trouble building my Angular 2 app with Angular-cli beta 14, despite it working perfectly fine with systemjs beforeEach

I have been using VSCode on Windows 10 to work on an app developed in Angular 2 final version, which runs smoothly with systemjs. Recently, I upgraded to angular-cli beta 14 webpack version and followed the steps outlined in the upgrade document here. How ...

The value of 'useFonts' cannot be changed as it is a read-only property in expo-fonts

Struggling with 'useFonts' being read-only and unable to assign In my Expo project using React Native (TypeScript), I encounter the issue of trying to import a .ttf font file. My attempt was to create a custom hook called "useFont" to pre-load ...

Enhancing Javascript HREF links

Occasionally, I visit URLs that have page numbers indicated in the HREF. As these page numbers change, keeping track of the last one visited becomes a challenge. Cutting and pasting into a text file is a temporary solution; therefore, I intend to replace i ...

Guide on implementing ng-repeat within a nested JSON structure in your Ionic app

Struggling with implementing ng-repeat in a nested json object. { "title": "Important message 01", "img": "any url image here", "authorPhoto": "http://lorempixel.com/40/40/people/4/", "author": "John Doe", "datePos ...

Challenge with Filter Functionality when Activating Button

Can you help me implement a search filter using buttons with the Isotope Plugin? For example, entering a search value in an input field and then clicking a search button to display the search results. How can I achieve this using buttons? Below is the H ...

The hardware acceleration feature is not functioning correctly

When I set android:hardwareAccelerated="false, the video ends up running in the background instead of the foreground. However, if I change it to android:hardwareAccelerated="true, the marquee text and other static images start flickering. ...

employing the join method with a property of an object

After trying to modify my nested array by using the join method and adding a line break with \n, I encountered an issue: const exampleArray = [["Hello"], ["world"], ["example"]] .map((el) => el) .join("&bs ...

Updating the parent navigation bar after a successful login in a child component in Angular4

In my Angular4 project, I have set up a login page. Within the parent app.component file, I have included a navigation bar with login and signup buttons. Upon successful login, the login and signup buttons should be hidden, and the username should appear i ...

Instructions on how to activate scrolling for a div element that becomes visible upon clicking a button

When a button in the navbar is clicked, a div appears with a height that exceeds the viewport/page size. How can I enable scrolling for the entire page when this happens? I attempted to use overflow: scroll, but it only applied scrolling to the internal d ...

Backbone - NestedModels - Issues with nested sets not triggering 'change' event

I have incorporated the Backbone nested plugin into my project. The way I set up my binding is as follows : var view = Backbone.View.extend({ initialize: function(params) { this.model.bind('change', _.bind(this.rerender, this)); ...

Utilize one ajax response for three distinct sections/divisions

After making an ajax call, I am receiving a total of 27 results that I need to divide equally into three sections, with 9 results in each. The sections are displayed below: HTML: <div id="content"> <section> </section> <s ...

What is the best way to emphasize case-insensitive searchtext matches in JavaScript?

If I have data containing words like Krishna, krishna, KRISHNA and I enter the search text as 'krish', it will retrieve all three words. However, when I want to highlight the matching result, only the exact matching part of the string is highligh ...

Conceal choices within a Vimeo video

Is it possible to conceal specific elements such as the title, logo, like and watch buttons on a Vimeo video while still displaying the play/pause button? I attempted to use CSS and JavaScript to accomplish this, but was unsuccessful due to the video being ...

In React Router version 4, it is stated that each router is only allowed to have a single child element when using multiple routes

I'm currently working on implementing a sidebar alongside the main content area using react-router-dom. Instead of just rendering <Sidebar/> directly, I want to pass it the location prop due to another issue where clicking a <Link/> in the ...

At what point does the promise's then function transition to being either fulfilled or rejected?

When dealing with promises in JavaScript, the then() method returns a promise that can be in one of three states: pending, fulfilled, or rejected. You can create a promise using the resolved and rejected methods to indicate when it should be fulfilled or r ...

Tips for performing multiple ajax requests simultaneously to retrieve JSON data from various URLs

I'm currently facing the challenge of needing to make 10 simultaneous AJAX requests to retrieve JSON content from various URLs. The responses for each request are independent of one another. How can I effectively manage these requests in this scenario ...

The Angular 2 view appears on the screen before the data finishes loading in the ngOnInit

Utilizing the github API in my angular 2 application, I encounter an issue where the view renders before ngOnInit has finished loading data. This leads to a Cannot read property of undefined error. The relevant portion of my code is as follows: ngOnInit() ...

Exploring the method of iteratively pairing Typescript test files with a NodeJS native testrunner

A new native test runner was recently introduced in NodeJS. I have managed to run typescript tests using ts-node or tsx node --loader ts-node/esm --test **/*.test.ts node --loader tsx --test **/*.test.ts However, a significant issue I encountered is tha ...

Steps for designing a movable image

I'm looking to implement a feature where the user can drag and drop an image anywhere on the page. Once the user places the image, its position will be saved so that when they revisit the page, it will be in the same location. Thank you! ...