Search code examples
javascriptapiasynchronousbinancecryptocurrency

Why on printing I get value of null from API call in Loop


COIN LIST is an array of crypto coins(["BTCUSDT",...]). I try to get the price using getPriceAction and RSI from getRSI and these two functions are working when I try to console DATA. But when I try to print the response after the completion of the loop. It prints the empty array and the length is 0 of this array. I want to store the DATA object (consisting of SYMBOL, closing price and RSI) as an element in the response array

 import { COIN_LIST } from "./COIN_LIST.js";
    import { getPriceAction } from "./PRICE_ACTION.js";
    import { getRSI } from "./RSI.js";

async function main() {
  try {
    let response = await [];
    await COIN_LIST.forEach((element, i) => {
      setTimeout(() => {
        let data = { symbol: element };
        getPriceAction(element, "4h").then((res) => {
          data.closingPrice = res;
          getRSI(res).then((res) => {
            data.RSI = res.reverse();
            data.closingPrice = data.closingPrice.reverse();
            response.push(data);
            console.log(data)
          });
        });
      }, i * 1000);
    });
    console.log(response);
  } catch (error) {
    console.log(error.message);
  }
}
main();

Solution

  • If you want to use async/await properly for your code, then use async/await, don't use .then/.catch as well

    Some notable changes

    there is no setTimeout of increasing seconds ... just waiting 1 second after one result before getting the next - far cleaner, and if one request happens to take a lot longer, you won't end up with two requests at once (which may be an issue if the API is rate limited)

    no .then ... use async/await OR .then/.catch - very rare to need both in the one function

    don't use forEach with async/await ... it never does what you want, and creating an array of Promises inside a .forEach is extremely naive, you may as well use .map instead! then you can await Promise.all(xxx.map(.....)) - but that's useful for concurrent requests, not so much for serial requests like your code does

    import { COIN_LIST } from "./COIN_LIST.js";
    import { getPriceAction } from "./PRICE_ACTION.js";
    import { getRSI } from "./RSI.js";
    
    async function main() {
        try {
            const wait = (ms) => new Promise(resolve => setTimeout(resolve, 1000));
            let response = []; //---> don't need that `await`
            for (let element of COIN_LIST) {
                let data = { symbol: element };
                data.closingPrice = await getPriceAction(element, "4h");
                const res = await getRSI(data.closingPrice);
                data.RSI = res.reverse();
                data.closingPrice = data.closingPrice.reverse();
                response.push(data);
                console.log(data);
                await wait(1000);
            }
            console.log(response);
        } catch (error) {
            console.log(error.message);
        }
    }
    main();
    

    the await wait(1000) could be tweaked depending on the rate limiting of the API ... if the rate limit applies to when the request is made, you could make a function that is smart about the delay between requests.

    The code this way assumes the rate limit is based on the period between previous response to next request.