Typescript Function completes execution before all promises resolved with Promise.all

I have a code snippet that looks like this:

export class MyHandler {
    entry = async (data: string[]): Promise<Map<string, string>> {
        const response: Map<string, string> = new Map();
        Promise.all(
            data.map(async (item) => {
                const apiGetDataRequest = {
                    data: item
                };
                const apiGetDataResponse = await this.client.apiCall(apiGetDataRequest);
                return apiGetDataResponse.data;
            });
        ).then((results) => {
            for (const result of results) {
                const value = myFirstMethod([1, 2, 3]);
                response.set(result, value);
            }
        });

        return response;
    };

    myFirstMethod = (items: number[]): string {
        const result = mySecondMethod(items, 'Test');
        console.log(result);
        return result;
    };

    mySecondFunction = (items: number[]): string {
        let finalResult = "";
        Promise.all(
            items.map(async (item) => {
                const apiCallRequest = {
                    data: item
                };  
                const apiCallResponse = await this.client.apiCall(apiCallRequest);
                return apiCallResponse.data;
            });
        ).then((results) => {
            for (const result of results) {
                finalResult = finalResult + ', ' + result;
            }
        });

        return finalResult;
    };
}

The problem I am facing is that mySecondFunction returns before all promises are completed, causing the result variable in myFirstMethod to always be an empty string. How can I make sure that mySecondFunction waits for all promises to finish before returning?

Answer №1

Your mySecondFunction method appears to be synchronous, so you will need to ensure that you properly use the await keyword with Promise.all.

mySecondFunction = async (items: number[]): string {
    let finalResult = "";
    await Promise.all(
        items.map(async (item) => {
            const apiCallRequest = {
                data: item
            };
            const apiCallResponse = await this.client.apiCall(apiCallRequest);
            return apiCallResponse.data;
        });
    ).then((results) => {
        for (const result of results) {
            finalResult = finalResult + ', ' + result;
        }
    });

    return finalResult;
};

Answer №2

Even though Matthieu's response is accurate, you can upgrade `mySecondFunction` without utilizing `async`. This is because `async` is essentially just a syntactic sugar for a function that returns a Promise. Here is an example:

// not async
mySecondFunction = (items: number[]): string {
    // Return the chained promise.
    return Promise.all(
        items.map(async (item) => {
            const apiCallRequest = {
                data: item
            };
            const apiCallResponse = await this.client.apiCall(apiCallRequest);
            return apiCallResponse.data;
        })
    ).then((results) => {
        // Style nit: Move the definition of finalResult closer to its use.
        let finalResult = "";
        for (const result of results) {
            finalResult = finalResult + ', ' + result;
        }
        // Return finalResult within the promise chain, so this becomes
        // the result of the Promise the function returns.
        return finalResult;
    });
};

If your environment supports `async`/`await`, which it does since you're using it inside `map`, then there isn't much reason to choose this style over the other. However, the benefit of `async` is to avoid explicit promise chains with `then`, but in your code, you still have one. It may be easier to read if you stick to one style.

mySecondFunction = async (items: number[]): string {
    let finalResult = "";
    let results = await Promise.all(
        items.map(async (item) => {
            const apiCallRequest = {
                data: item
            };
            const apiCallResponse = await this.client.apiCall(apiCallRequest);
            return apiCallResponse.data;
        })
    );

    // Instead of putting this in a `then` method,
    // just use the results you've awaited above.
    for (const result of results) {
        finalResult = finalResult + ', ' + result;
    }

    return finalResult;
};

(Regardless of the style chosen, remember to modify `myFirstMethod` to `await` the result of `mySecondFunction` and update your `entry` function to ensure it awaits the result of `myFirstMethod`. This is the nature of asynchronous code: everything you depend on must be waited for.)

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

Retrieving user input from a personalized autocomplete cell editor

Struggling to create a custom autocomplete feature with ag grid. Unable to update the returned value with the selected item from the dropdown list. In simpler terms: I input 'A' => Receive a list of options => Select one => The input s ...

What could be causing my NodeJS Backend to not retrieve the data properly?

For a current project, I am tasked with building a mobile application using Flutter for the frontend and NodeJS for the backend. To facilitate this, I have acquired a VPS from OVHcloud running Ubuntu 20.04. Following various tutorials, I set up a server as ...

Beware: React higher-order component alert - Caution: It is not possible to modify a component from within the function body of another

Recently, I crafted a simple higher-order component that retrieves data from an API endpoint and handles loading, error messages, or displaying content upon success. Although the component functions correctly, it triggers the following error message: War ...

Exploring the Vue 3 Composition API with TypeScript and Working with Object Keys

Exploring the Vue-3 composition API and seeking guidance on utilizing types with TypeScript in Vue. Looking for more detailed information on how to define object properties and specify keys in TypeScript within the composition API, as the current document ...

Dealing with null-safe operators issues has been a challenge for me, especially while working on my Mac using

Hey everyone! I'm encountering errors when using null sage operators in TypeScript. Can someone help me figure out how to solve this issue? By the way, I'm working on Visual Studio Code for Mac. https://i.stack.imgur.com/huCns.png ...

The value of a Mat dialog within a mat table will remain consistent when an addrow action is performed

After attempting to open my table, here is what I discovered: https://i.sstatic.net/F3zCZ.png When opening the dialog box, it displays a list on my table. https://i.sstatic.net/SyJJk.png However, upon clicking "add row," it duplicates the value for me. ...

Create a visual representation after inserting

I have created an input feature that allows me to paste images by using CTRL-V. The process involves copying an image from the browser and pasting it into the input field using CTRL-V. The result is an image in base64 format. Is there a way to manipulate ...

I'm having trouble viewing the unique Google Map design on my application

I have recently customized Google maps following the guidelines in this documentation: https://developers.google.com/maps/documentation/javascript/styling For styling, I utilized the Cloud tool and opted for the available template instead of using JSON st ...

Fill an array with four distinct, random objects without any duplicates using ES6+ and apply specific filters

I could use some assistance enhancing a function that populates an array with 4 random, non-repeating recipe objects based on their IDs. However, I now need to incorporate an additional requirement where the recipes must include one breakfast, one lunch, o ...

Encountering Angular Material UI issues following the transition from version 11 to 12

I am currently in the process of updating my Angular application from version 11 to 12, integrating Angular Material, and encountering some error messages: Error No.1 - Error NG8002: Cannot bind to 'ngModel' as it is not a recognized property of ...

Implementing Avro file deserialization in a React application

I could really use some assistance with deserializing an avro file in a react application. I attempted to use the avsc npm package, but now I have encountered an error. const avro = require('avsc') ...... avro.createFileDecoder('./abc.avro&a ...

Is there a TypeScript type that represents a subset of the keys of another type?

When given an object, is it possible to create a second typed object that contains only a subset of the original keys with different value types? I attempted to use Partial<keyof ...>, but it did not have the desired effect. Is there another approach ...

A factory pattern for Typescript modules

In the world of Node.js, one popular method is to utilize the module factory pattern. Let's take a look at an example: m.js function factoryMethod(module) { // perform some actions and return object initialized with module parameter } app.js var ...

Ensure that the method is triggered

I have a builder class that implements an interface which it is expected to build. However, I would like to enforce one method of this class to be called at compile time, rather than runtime. The class is designed to be used as a chain of method calls and ...

Saving large amounts of data in bulk to PostgreSQL using TypeORM

I am looking to perform a bulk insert/update using TypeORM The Test entity is defined below: export class Test { @PrimaryColumn('integer') id: number; @Column('varchar', { length: 255 }) testName: string; } I have the f ...

Having trouble locating the draggable element with identifier x in react-beautiful-dnd

Encountering an issue with react-beautiful-dnd on my reactjs page. I've sourced my code from this link and made some minor tweaks to the content of each "dragable"/"row" Problem Update: The problem occurs when attempting to drag ...

utilizing TypeScript with the map function to destructure extensive objects

As a relative newcomer to TypeScript, I find myself struggling with annotations within a project that pulls various data from a GraphQL endpoint. Considering the potential for changes in the data structure down the line, I have opted to use Apollo Client ...

Pattern for path configuration in tsconfig.json

Is there a way to dynamically include files based on path patterns? Currently, my scripts contain lines like this: import '../../../../myresource/src/shared/my-interface' However, I'm interested in simplifying it to something like: import ...

Using Typescript to map a string to an enum

Having trouble mapping a string to an enum and receiving the error message TypeError: Cannot convert undefined value to object export enum CallErrorType { UNAUTHENTICATED, } const handler: { [key in CallErrorType]: (message: string) => void; } = { ...

Encountering an issue with the Link component in Next.js when using TypeScript: The href prop in <Link> should be a string or object, but it is currently undefined

Here is my customized link component code implemented using TypeScript: ... const NavLink: React.FC<{ activeClassName: string; className: string; href: string; clickEvent?: MouseEventHandler; onClick?: MouseEventHandler; title: string; }> ...