3

I'm trying to use Node.js to get a response from an API, I want to clean the API response and use the result.

So to access the first API I have the following code.

To store and use the result I believe I need to store the JSON output globally.

However, I can't work out how to do this.

Example -

var request = require('request');

request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function(err, res, json) {
if (err) {
    throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
    delete car2go[i].address;
    delete car2go[i].charging;
    delete car2go[i].exterior;
    delete car2go[i].interior;
    delete car2go[i].smartPhoneRequired;
    delete car2go[i].vin
    car2go[i].vendor = 'car2go';
    car2go[i].city = 'wien';
    car2go[i].carmake = 'Smart';
    car2go[i].carmodel = 'Fortwo';
}
console.log(car2go);
});

This prints the desired result however I know that this is because my variable is defined within the function.

I want to access the variable outside of the function.

To test if I could do this I changed the code to -

var request = require('request');

request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function(err, res, json) {
if (err) {
    throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
    delete car2go[i].address;
    delete car2go[i].charging;
    delete car2go[i].exterior;
    delete car2go[i].interior;
    delete car2go[i].smartPhoneRequired;
    delete car2go[i].vin
    car2go[i].vendor = 'car2go';
    car2go[i].city = 'wien';
    car2go[i].carmake = 'Smart';
    car2go[i].carmodel = 'Fortwo';
}
});

console.log(car2go);

But if I do this I get

ReferenceError: car2go is not defined

I am running Node v0.12.2 on Mac OS Yosemite (10.10.3).

Admittedly I am very new to node and I am more familiar with R, Python and PL SQL.

0

2 Answers 2

1

There is no way to get reference to it outside of the callback function because the console.log line runs before the callback function is invoked. The reason you have to pass a callback function into the request API is because the request library needs to invoke that function when it's done making the request. Meanwhile, your app moves on and does other things (such as running that console.log line) while it waits for the callback function to fire.

That said, there are a number of ways to deal with asynchronous code. My favorite way is with promises. I use a library called bluebird for handling promises.

var request = require('request');
var Promise = require('bluebird');
var requestP = Promise.promisify(request);

The call to Promise.promisify(request) returns a new function that doesn't take a callback function, but instead returns a promise.

requestP({ url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true })
  .spread(function(res, json) {
    var car2go = json.placemarks;
    for (i = 0; i < car2go.length; i++) {
      delete car2go[i].address;
      delete car2go[i].charging;
      delete car2go[i].exterior;
      delete car2go[i].interior;
      delete car2go[i].smartPhoneRequired;
      delete car2go[i].vin
      car2go[i].vendor = 'car2go';
      car2go[i].city = 'wien';
      car2go[i].carmake = 'Smart';
      car2go[i].carmodel = 'Fortwo';
    }
  })
  .then(function (car2go) {
    console.log(car2go);
  })
  .catch(function (err) {
    console.error(err);
  });

Note: .spread is the same as .then except if the resolved value is an array (which it will be because the callback passed to the request library accepts 2 arguments, which bluebird will translate into an array that the promise resolves to) .spread will split up the array back into multiple arguments passed into the function you give to .spread.

Promise.resolve(['hi', 'there']).then(function (result) {
  console.log(result); // "['hi', 'there']"
});

Promise.resolve(['hi', 'there']).spread(function (str1, str2) {
  console.log(str1); // 'hi'
  console.log(str2); // 'there'
});

You're not going to be able to return that value all the way back out to the same context from which you began the asynchronous call, but you can at least write code that looks somewhat synchronous when using promises.

Without promises you'll be forced to call functions from within functions from within functions from within functions ;)

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the excellent response. This still means that any further work I would want to with my JSON, I would have to either call a function within the request function or I would have to continue to nest my workings deeper and deeper into the current code..
Correct. That's why promises exist, so that we can hang on to some of the things we're used to like try/catch, and to prevent the pyramid of doom that stems from continuously nesting callbacks.
Ironically, I read an extremely well-written blog post this morning about promises. I suggest you check it out :D. It's not really about teaching promises, but it shows some great examples and highlights some excellent rookie mistakes. pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
0

The response is asynchronous. That means the callback function gets called sometime LATER in the future so your console.log(car2go) is executing BEFORE the callback has even been called.

The only place you can reliably use the response is inside the callback or in a function called from the callback. You cannot use it the way you are trying to. Using asynchronous responses in Javascript requires programming in an asynchronous fashion which means processing results and using results IN the asynchronous callbacks only.

Here's where the console.log() should be:

var request = require('request');

request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function (err, res, json) {
    if (err) {
        throw err;
    }
    car2go = json.placemarks;
    for (i = 0; i < car2go.length; i++) {
        delete car2go[i].address;
        delete car2go[i].charging;
        delete car2go[i].exterior;
        delete car2go[i].interior;
        delete car2go[i].smartPhoneRequired;
        delete car2go[i].vin
        car2go[i].vendor = 'car2go';
        car2go[i].city = 'wien';
        car2go[i].carmake = 'Smart';
        car2go[i].carmodel = 'Fortwo';
    }
    // here is where the result is available
    console.log(car2go);
});

5 Comments

And it will be asked a zillion more because there will always be new people entering the fold.
@AlexFord - yep. I removed that part of the answer.
@tadman - did you by chance put your comment in the wrong place? I don't know what 32-bit integer has to do with my answer.
I guess my joke that a zillion can't be represented in a 32-bit integer didn't work.
@tadman - Went right over my head. Anyway, the zillion comment was edited out of my answer awhile ago.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.