0

I asked this question earlier and it got associated with this question ( How do I return the response from an asynchronous call? ) But It doesn't resolve my problem

i am very new to node.js and i'm having some error that i can't deal with or find any help that i understand. I used express-generator to initialize the app. I'm trying to send and object i get through an api call to the front end. I wrote a class "StockClass.js" with a function makeApiCall() which makes the call and returns the object. But when i call that function in the router i get "undefined". Heres the code

//==== StockClass.js ===

const yahooFinance = require("yahoo-finance");
class stockClass {
    static makeApiCall(symbol) {
        yahooFinance.quote(
            {
                symbols: [symbol],
                modules: ["price", "summaryDetail"],
            },
            function (err, quotes) {
                if (err) { console.log(err) }
                console.log(quotes)
                return quotes;
            }
        );
    }
}

module.exports = stockClass;

//=====index.js======

const StockClass = require("../handlers/StockClass");

router.get("/new", function (req, res) {

   let quotes = StockClass.makeApiCall("AAPL");
   console.log(quotes);
   res.render('path', { quotes });
});

The console.log in the StockClass.js logs the object (quotes) correctly while the console.log in index.js logs "undefined".

Link below explains the yahoo-finance api call. https://www.npmjs.com/package/yahoo-finance

==========================================================================

I also tried using a middleware and attaching the data to the response object like this

//==========middleware=========

const yahooFinance = require("yahoo-finance");

module.exports = {

    makeApiCall: (symbol) => {
        return function (req, res, next) {
            yahooFinance.quote(
                {
                    symbols: [symbol],
                    modules: ["price", "summaryDetail"],
                },
                function (err, quotes) {
                    if (err) { console.log(err) }
                    res.stockObj = quotes;
                    console.log(res.stockObj);
                }
            );
            next();
        }
    }
}

//======= index.js =========
const handler = require("./handlers/stockUtils");

    router.get("/new", handler.makeApiCall("AAPL"), function (req, res) {

       let quotes = res.stockObj;
       console.log(quotes);
       res.render('path', { quotes });
    });

And the results are the same. The console.log in the middleware function logs the correct object, but the console.log in index.js logs undefined

Resonance
  • 13
  • 4
  • Its because the API response takes time and due to async behavior you are getting undefined, you need to add some callback or promises while calling API from frontend just like you have added in server-side. – Parveen yadav Apr 17 '20 at 05:24

1 Answers1

0

This is a classic case of asynchronous programming (non blocking) and coming from synchronous programming background (like python, java, c etc) I too faced this problem of understanding the flow of the code in javascript. It took me awhile but once you grasp the way javascript language behaves, you will be able to see how the JS engine interprets your code and how it gets executed. Here are some links you might find useful:

Anyways, there are three ways to solve your problem.

  1. Callback (old way)
  2. Promises (somewhat new way)
  3. Async/Await (latest way)

Callback Way

I have simplified your modularised code to wait for async operation finish and then make a callback. I will let you split it again and use it in express or whatever web framework you are using.

const yahooFinance = require("yahoo-finance");

class StockClass {
  static makeApiCall(symbol, callback) {
    yahooFinance.quote(
      {
        symbols: [symbol],
        modules: ["price", "summaryDetail"],
      },
      function (err, quotes) {
        if (err) {
          console.log(err);
        }
        callback(quotes);
      }
    );
  }
}

function toBeExecutedWhenDone(data) {
  console.log(data);
}

// since its a static function we are calling it directly from Class instance
StockClass.makeApiCall("AAPL", toBeExecutedWhenDone); // this function can be your response

In the code above, you execute or return your response only when you have the data/quotes (or you can say when the async operation finishes). I'll let you explore more later.

Promises Way

Promises allow you to return something called Promise! Promises gets resolved when the async operation finishes. Here is how your code looks.

const yahooFinance = require("yahoo-finance");

class StockClass {
  static makeApiCall(symbol) {
    return new Promise((resolve, reject) => {
      yahooFinance.quote(
        {
          symbols: [symbol],
          modules: ["price", "summaryDetail"],
        },
        function (err, quotes) {
          if (err) {
            reject(err);
          }
          resolve(quotes);
        }
      );
    });
  }
}

// since its a static function we are calling it directly from Class instance
StockClass.makeApiCall("AAPL")
  .then((data) => console.log(data)) // you could add your return your response instead of console log.
  .catch((err) => console.error(err));

Async Await way!

Async await requires your function to be declared with async keyword. You can look at express guides as it already supports async functions. Here is how the snippet looks like.

const yahooFinance = require("yahoo-finance");

class StockClass {
  static makeApiCall(symbol) {
    return new Promise((resolve, reject) => {
      yahooFinance.quote(
        {
          symbols: [symbol],
          modules: ["price", "summaryDetail"],
        },
        function (err, quotes) {
          if (err) {
            reject(err);
          }
          resolve(quotes);
        }
      );
    });
  }
}

async function giveMeQuotes() {
  try {
    const stock = await StockClass.makeApiCall("AAPL");
    console.log(stock); // this gets executed when above async operation finishes
  } catch (error) {
    console.error(error);
  }
}

// executing to show that it works...
giveMeQuotes();

Hope it helps!!! Good luck!

dina
  • 937
  • 1
  • 12
  • 29
  • Please don't forget to accept the answer if it's what you were looking for – dina Apr 17 '20 at 04:44
  • Hi, all three of these solutions worked great for me. Thanks and i appreciate you. Also im new on stackoverflow, how do i accept the answer? – Resonance Apr 17 '20 at 18:18