0

I've got this function

function parseLink(link) {
    var product;
    request(link, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var $ = cheerio.load(body);

            // title
            var title = $('h1').text();
            if (!title)
                var title = $('title').text();

            var description = $('meta[name="description"]').attr('content');

            product = new Product(link.trim(), title.trim(), description.trim());
        }
    });
    console.log(product);
    return product;
}

And I don't understand why when I do console.log(product) outside of the request call, I've got undefinded but inside, I can see my product.

I learn lot of things about scopes in javascript and I don't understand, causse I defined product in the top function. I need to return this variable for get it in another function, if do the return inside request I've got of course an undefined so I need to do that outside... Thank you

  • Because you did not assign a value to your variable `product` in the top function and `console.log()` don't wait assignment from the request function. – hhh May 01 '17 at 21:05
  • Possible duplicate of [How do I return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Heretic Monkey May 01 '17 at 21:08
  • Where is your `request` function defined? – Cedric Ipkiss May 01 '17 at 21:14
  • Thanks but I need to return the value like `var linkParsed = parseLink(data[0]);`z –  May 01 '17 at 21:23

2 Answers2

0

javascript does not run the code like c or php where you can be sure that the next line of code runs when the previous is ready. In your case request is an asynchronous function so the two lines

console.log(product);
return product;

are mostly run before your request function is ready. In that case you can not just return some value from your parseLink function. You have two possibilities here:

  1. use promises: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise

  2. use a callback:

like this:

function parseLink(link, callback) {
    var product;
    request(link, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var $ = cheerio.load(body);

            // title
            var title = $('h1').text();
            if (!title)
                var title = $('title').text();

            var description = $('meta[name="description"]').attr('content');

            product = new Product(link.trim(), title.trim(), description.trim());
            callback(product);
        }
    });
}

and you run the code like

parseLink('http://...', function(product) { /* do something with the product */ });

ps: the use of callbacks is a lot easier imo, but in some cases you can to separate the scope for example if you run it in a for loop

Dimitri L.
  • 4,499
  • 1
  • 15
  • 19
  • Thank you, I call parseLink from another function. I want to do something like that in this function: `var linkParsed = parseLink(data[0]);` –  May 01 '17 at 21:13
  • Like I have written it is not possible to directly return a value from an asynchronous function. At the end of my answer is an example with a callback function. Miguel also provided an example with a promise – Dimitri L. May 01 '17 at 21:16
  • What about C? You can pass a pointer-to-function as an argument in a call and you won't have an idea whether the function has been called by the time this call returns. – bipll May 01 '17 at 21:17
  • Thanks but I need to return the value like `var linkParsed = parseLink(data[0]);` –  May 01 '17 at 21:23
0

request is an asynchronous call so this procedure gets pushed to the event queue which will run once the current call stack has finished. console.log prints undefined because that is the default value for unassigned variables.

You must use callbacks or promises if you need to return the value from the asynchronous call. Here's an example using a Promise:

function parseLink(link) {
  return new Promise((resolve, reject) => {
    request(link, function(error, response, body) {
      if (error) return reject(error);

      if (response.statusCode !== 200) {
        return reject(new Error('Not OK'));
      }

      var $ = cheerio.load(body);

      var title = $('h1').text() || $('title').text();
      var description = $('meta[name="description"]').attr('content');
      var product = new Product(link.trim(), title.trim(), description.trim());

      resolve(product);
    });
  });
}

parseLink('http://example.com')
  .then(product => {
    console.log(product);
  })
  .catch(error => {
    console.error(error);
  });
Miguel Mota
  • 20,135
  • 5
  • 45
  • 64
  • Thanks but I need to return the value like `var linkParsed = parseLink(data[0]);` –  May 01 '17 at 21:28
  • @Tech you'll need to do a synchronous request but it's highly not recommended https://www.npmjs.com/package/sync-request – Miguel Mota May 01 '17 at 21:30