← All Articles A Product of Kinsa Creative

Executing Async Tasks in JavaScript

Promise / Then

Create a promise, then use it:

async function myAsyncFunction( ... ) { ... }

const myPromise = myAsyncFunction( ... );

myPromise.then((resultOfMyFunction) => { 
    // do something with the result
})
.catch((error) => {
    console.error("Error fetching data:", error);
});

In React

Use useEffect()

async function myAsyncFunction( ... ) { ... }

useEffect(() => {
    myAsyncFunction();
}, [ ... ]);

That can be wrapped in a function to make it portable, for example to fetch an object from an API:

interface Object {
    id: number;
    ...
}

interface MyOptions {
    objectId?: number;  // an object ID
}

interface MyReturnType {
    objectData: Object | null;
    isLoading: boolean;
    error: string | null;
    refetch: () => void;
}

export default function useObjectData({
    objectId
}: MyOptions): MyReturnType {
    const [objectData, setObjectData] = useState<Object | null>(null);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);

    const fetchData = async () => {
        if (!reviewId || !userId || !enabled) {
            return;
        }

        setIsLoading(true);
        setError(null);

        try {
            const response = await fetch(
                `${API_ENDPOINTS.BASE_URL}/objects/${objectId}/`,
                {
                    headers: {
                        ...
                    },
                },
            );

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }

            const objects = (await response.json()) as Object[];

            if (objects && objects.length === 1) {
                setObjectData(objects[0]);
            } else if (objects.length < 1) {
                setError("Object not found");
            } else {
                setError("Multiple objects returned");
            }
        } catch (error) {
            setError(
                error instanceof Error ? error.message : "Failed to fetch something data",
            );
        } finally {
            setIsLoading(false);
        }
    };

    // The hook will automatically fetch the object data when the `objectId ` changes...

    useEffect(() => {
        fetchData();
    }, [objectId]);

    // ...or, manually refetch using the `refetch` function if needed.

    const refetch = () => {
        fetchData();
    };
}

Executing Multiple Asynchronous Tasks

Async tasks can be called in parallel using Promise.all() and Promise.allSettled(). According to _mdn, Promise.allSettled() is "typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, or you'd always like to know the result of each promise" while Promise.all() "may be more appropriate if the tasks are dependent on each other, or if you'd like to immediately reject upon any of them rejecting."

Promise.all()

Here, I'm using Promise.all() to fetch items from three different data stores in parallel and assign each to a variable before proceeding to the next step. If any of of the fetches fail, everything fails:

// Run independent operations in parallel
const [itemData, reviewData, userData] = await Promise.all([
    getItemById( ... ),
    getReviews( ... ),
    firestoreDatabase
        .collection( "users" )
        .doc( userId )
        .get()
]);

Promise.allSettled()

Here, I'm using Promise.allSettled() to send two emails in parallel.

const emailResults = await Promise.allSettled([
    sendMail( ... ),
    sendMail( ... ),
]);

// Handle results individually
emailResults.forEach((result, index) => {
    const emailType = index === 0 ? "customer" : "operations";

    if (result.status === "rejected") {
        console.error(`Failed to send email to ${emailType}:`, result.reason);
        // Log but don't throw - allows other operations to continue
    } else {
        console.log(`Successfully sent email to ${emailType}`);
    }
});

Feedback?

Email us at enquiries@kinsa.cc.