0

When I make a "GET" request from the client to the server the server should make a axios.get() call to a stock API to retrieve data for an array of tickers. When I console.log the results it seems to be working fine but the array doesn't seem to save, like it gets wiped out and comes back to the client as empty. I think I might be messing this up with async/await.

async function currentPrice(ticker) {
    const apiURL = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${ticker}&apikey=${API_KEY}`;
    let price;

    await axios.get(apiURL).then(data => {
        try { 
            price = data.data["Global Quote"]["05. price"];
        } catch (error) {
            console.log(error)
        }
    })
    return price;
};

app.get("/refresh", redirectLogin, (req, res) => {
    const {
        user
    } = res.locals;
    var array = [];
    connection.query(`SELECT * FROM holdings WHERE user_name = '${user.user_name}' AND quantity > 0`, (err, results) => {
        if (err) throw err;
        results.forEach(holding => {
            currentPrice(holding.ticker).then(data => {
                var updatedTicker = {
                    ticker: holding.ticker,
                    description: holding.description,
                    price_acquired: holding.price_acquired,
                    market_price: data,
                    delta: parseFloat(this.market_price) - parseFloat(this.price_acquired),
                    quantity: holding.quantity,
                    trade_date: holding.date_acquired
                }
                array.push(updatedTicker);
                // console.log(array);
                console.log(updatedTicker.market_price)
            })
        })
        res.json(array)
    })
})
Roamer-1888
  • 19,138
  • 5
  • 33
  • 44

1 Answers1

0

You are calling res.json(array) before any of your currentPrice().then(...) calls have finished, thus the array is still empty.

There are a number of different ways to solve this. Probably the simplest is to change for .forEach() loop to a plain for loop and then use async/await to serialize each of your calls to currentPrice():

function currentPrice(ticker) {
    const apiURL = `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${ticker}&apikey=${API_KEY}`;

    return axios.get(apiURL).then(data => {
        try {
            return data.data["Global Quote"]["05. price"];
        }
        catch (error) {
            console.log(error);
            throw error;
        }
    });
}


app.get("/refresh", redirectLogin,  (req, res) => {

    const { user } = res.locals;
    connection.query(`SELECT * FROM holdings WHERE user_name = '${user.user_name}' AND quantity > 0`,  async (err, results) => {
        if (err) {
            console.log(err);
            res.sendStatus(500);
            return;
        }

        try {
            const array = [];
            for (let holding of results) {
                let data = await currentPrice(holding.ticker);
                let updatedTicker = {
                    ticker: holding.ticker,
                    description: holding.description,
                    price_acquired: holding.price_acquired,
                    market_price: data,
                    delta: parseFloat(this.market_price) - parseFloat(this.price_acquired),
                    quantity: holding.quantity,
                    trade_date: holding.date_acquired

                }
                array.push(updatedTicker);
            }
            res.json(array);
        } catch(e) {
            console.log(e);
            res.sendStatus(500);
        }
    });
});

Various changes:

  1. Simplified the currentPrice() function to just return the axios promise directly
  2. Appropriately reject in currentPrice() if there's an error so the caller sees the error.
  3. Add proper error handling (sending an error response), if the db query fails.
  4. Switch .forEach() loop to a for loop so we can use await to serialize the calls to currentPrice() so we can more easily know when they are all done.
  5. Add error handling and sending of an error response if currentPrice() has an error.
  6. Call res.json(array) only after all the now-serialized calls to await currentPrice() have completed.

FYI, a fully complete transformation here would switch to mysql2 so you can use the promise interface for connection.query() rather than the plain callback interface that you are using now. That would allow you to consolidate error handling to one place more easily.

jfriend00
  • 683,504
  • 96
  • 985
  • 979