-1

My code takes care of various scenarios and is quite long, so I have reproduced with a boiled down pseudocode below:

const sendOrder = async function (Order) {
    return new Promise(async (resolve) => {
        console.log("order about to be place for " + Order.id + " at " + isoTime());

        try {
            const orderResponse = await sendOrderToBroker(Order);
            console.log("order " + Order.id + "sent to broker at " + isoTime());

            //Update db with details about successful order placement
            models.orders
                .update({
                    //Order status details
                })
                .then((countRowsUpdated) => {
                    console.log(
                        `Order ${orderId} success - ${countRowsUpdated} row(s) updated at time ${isoTime()}`
                    );
                    resolve(countRowsUpdated);
                });
        } catch (error) {
            //Update db with details about failed order placement
            models.orders
                .update({
                    //failed order status details
                })
                .then((countRowsUpdated) => {
                    console.log(
                        `Order ${orderId} failure - ${countRowsUpdated} row(s) updated at time ${new Date().toISOString()}`
                    );
                    resolve(countRowsUpdated);
                });
        }
    });
};

const run = async function () {
    const ordersToBeProcessed = [];
    const Order = {};

    const orders = await models.orders.findAll(); //database request via sequelize

    orders.map(async function (order) {
        if (order.action === "entry") {
            Order = populateOrderObject(order);
        }

        if (order.action === "exit") {

    try {
        currentPosition = await fetchCurrentPosition(); //network request via axios. Cause of the bug. Edited to include try... catch block
    Order = populateOrderObject(order, currentPosition);
} catch (error) {
//Update db with details about failed fetch position
            await models.orders
                .update({
                    //failed fetch position details including error
                })
    }
            
            
        }

        ordersToBeProcessed.push(sendOrder(Order));
    });

    Promise.allSettled(ordersToBeProcessed).then((responses) => {
        responses.map((response) => {
            console.log(" response: " + response.status + ", value: " + response.value);
        });
        console.log("processedOrders.length is: " + processedOrders.length);
        console.log("Number of responses is: " + responses.length);

        if (orders.length === responses.length) {
            setTimeout(run, 500);
        }
    });
};

setTimeout(run, 500);

This is the output below. For reference, orders 361 and 362 are entry orders with order.action === "entry", while orders 360 and 364 are exit orders:

Order engine started: 2020-10-12T21:22:47.712Z
running orders at time 2020-10-12T21:22:48.232Z
order about to be placed for 361 at time 2020-10-12T21:22:48.302Z
order about to be placed for 362 at time 2020-10-12T21:22:48.302Z
24
order about to be placed for 360 at time 2020-10-12T21:22:48.838Z
order about to be placed for 364 at time 2020-10-12T21:22:48.839Z
order 361 sent to broker at 2020-10-12T21:22:48.909Z
Order 361 success - 1 row(s) updated at time 2020-10-12T21:22:48.918Z
order 362 sent to broker at 2020-10-12T21:22:48.927Z
Order 362 success - 1 row(s) updated at time 2020-10-12T21:22:48.930Z
 response: fulfilled, value: 1
 response: fulfilled, value: 1
processedOrders.length is: 4
Number of responses is: 2
order 360 sent to broker at 2020-10-12T21:22:49.181Z
Order 360 success - 1 row(s) updated at time 2020-10-12T21:22:49.185Z
order 364 sent to broker at 2020-10-12T21:22:49.185Z
Order 364 success - 1 row(s) updated at time 2020-10-12T21:22:49.197Z

When order.action is equal to "exit", the Promise.allSettled (and also Promise.all) resolves without those exit orders. So, only the entry orders are included in the responses. The exit orders eventually resolve but the script terminates because the conditional setTimeout is never fired. What can I do to achieve the desired objective of having all processed orders resolve before Promise.allSettled.then runs? How do I handle the extra network request in the exit orders as I believe this is where the issue is coming from? I think this has to do with the event loop and handling of microtasks, but I can't move forward from there.

  • 2
    Two things here: 1. You should [never use an `async` executor function for a promise constructor](https://stackoverflow.com/q/43036229). 2. It looks like you're using [the explicit promise construction antipattern](https://stackoverflow.com/q/23803743) – VLAZ Oct 12 '20 at 22:06
  • 1
    [Never pass an `async function` as the executor to `new Promise`](https://stackoverflow.com/q/43036229/1048572)! – Bergi Oct 12 '20 at 22:13
  • 1
    You're not handling errors from `fetchCurrentPosition` at all, and your `Promise.allSettled` will always resolve immediately as `ordersToBeProcessed` is empty after the `map` call - notice you're pushing into that array asynchronously! – Bergi Oct 12 '20 at 22:16
  • @Bergi Thanks for tips. I am relatively new to Javascript having dabbled in jQuery and Angular years ago, and I am still trying to understand async and promises. I will go through the links shared by you and VLAZ now. I am actually handling errors from fetchCurrentPosition, I just did not include that part in the pseudocode above. – nubianrover Oct 12 '20 at 23:04
  • We can only judge what we can see :-) – Bergi Oct 13 '20 at 00:40
  • @Bergi I edited that part to show what I am doing – nubianrover Oct 13 '20 at 02:14
  • Ah, well, doesn't change the answer… – Bergi Oct 13 '20 at 08:11

1 Answers1

0

What can I do to achieve the desired objective of having all processed orders resolve before Promise.allSettled.then runs?

Put them in the array of promises that Promise.allSettled is waiting for. Your loop over orders has an asynchronous part, and the exit orders are put in the ordersToBeProcessed array after you called Promise.allSettled immediately after the loop. The proper way to write that, using the map method, is

async function run() {
    const orders = await models.orders.findAll(); //database request via sequelize

    const ordersToBeProcessed = orders.map(async function (order) {
//        ^^^^^^^^^^^^^^^^^^^^^^
        const Order = order.action === "entry" ? populateOrderObject(order) :
                      order.action === "exit" ? populateOrderObject(order, await fetchCurrentPosition()) :
                      undefined;
        return sendOrder(Order);^
//      ^^^^^^
    });

    const responses = await Promise.allSettled(ordersToBeProcessed);
    for (const response of responses) {
        console.log(" response: " + response.status + ", value: " + response.value);
    });
    console.log("All " + ordersToBeProcessed.length + "orders got processed");
    setTimeout(run, 500);
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I solved the issue using a modified version of this answer, but the underlying concept remains the same, and it revolves around the usage of Array.map. Thanks @Bergi – nubianrover Oct 16 '20 at 19:54