Roka545
Roka545

Reputation: 3636

Wait for async function to return a value in Typescript.

I have a function (Function1) that calls another function (Function2) that returns a string.

I want Function1 to call Function2, but wait and get the return value of Function2. How do you go about doing that? Here's my code (which does not work as intended):

Here is my Function1:

async Function1() {
    const message = await this.dialogButtonPress();
    alert(message);
}

Here is my Function2:

async dialogButtonPress() : Promise<string> {
    const doneButtonPromise = new Promise((resolve) => {
        const doneButton = document.getElementById("done-button");
        const resolver = () => {
            resolve();
            doneButton.removeEventListener("click", resolver);
        }
        doneButton.addEventListener("click", resolver);
    });

    const cancelButtonPromise = new Promise((resolve) => {
        const cancelButton = document.getElementById("cancel-button");
        const resolver = () => {
            resolve();
            cancelButton.removeEventListener("click", resolver);
        }
        cancelButton.addEventListener("click", resolver);
    })

    doneButtonPromise.then(() => {
        console.log("DONE BUTTON PRESSED!");
        return "Confirm";

    });

    cancelButtonPromise.then(() => {
        console.log("CANCEL BUTTON PRESSED!");
        return "Cancel";
    });      
}

Upvotes: 3

Views: 17548

Answers (2)

Louis
Louis

Reputation: 151561

If you use Promise.race as the other answer suggests, you won't be cleaning up the event handler for the button that was not pressed. You'll be cleaning up only the handler for the button that was pressed. If it were me, I'd just combine the event handlers to ensure that everything is cleaned up when any button is pressed:

async dialogButtonPress(): Promise<string> {
    return new Promise<string>((resolve) => {
        const doneButton = document.getElementById("done-button")!;
        const cancelButton = document.getElementById("cancel-button")!;
        const resolver = (ev: Event) => {
            doneButton.removeEventListener("click", resolver);
            cancelButton.removeEventListener("click", resolver);
            // This is where we determine which button was clicked.
            resolve(ev.target === doneButton ? "Confirm" : "Cancel");
        }
        doneButton.addEventListener("click", resolver);
        cancelButton.addEventListener("click", resolver);
    });
}

And it could scale to any number of buttons:

async dialogButtonPress(): Promise<string> {
    return new Promise<string>((resolve) => {
        // You just need to expand this map in order to support additional buttons.
        const idToResponse: Record<string, string> = {
            "done-button": "Confirm",
            "cancel-button": "Cancel",
        };
        const buttons = Object.keys(idToResponse).map((id) => document.getElementById(id)!);
        const resolver = (ev: MouseEvent) => {
            for (const button of buttons) {
                button.removeEventListener("click", resolver);                    
            }
            // We index into idToResponse to convert the button's id to a specific response.
            resolve(idToResponse[(ev.target as Element).id]);
        }

        for (const button of buttons) {
            button.addEventListener("click", resolver);
        }
    });
}

Upvotes: 4

CRice
CRice

Reputation: 32276

Change

doneButtonPromise.then(() => {
    console.log("DONE BUTTON PRESSED!");
    return "Confirm";

});

cancelButtonPromise.then(() => {
    console.log("CANCEL BUTTON PRESSED!");
    return "Cancel";
});

To

return Promise.race([
    doneButtonPromise.then(() => {
        console.log("DONE BUTTON PRESSED!");
        return "Confirm";
    }),
    cancelButtonPromise.then(() => {
        console.log("CANCEL BUTTON PRESSED!");
        return "Cancel";
    })
]);

Upvotes: 2

Related Questions