Sync http request in Node [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I'm new to node and I'm trying to figure out the callbacks and the async nature of it.
I have this kind of function:
myObj.exampleMethod = function(req, query, profile, next) {
// sequential instructions
var greetings = "hello";
var profile = {};
var options = {
headers: {
'Content-Type': 'application/json',
},
host: 'api.github.com',
path: '/user/profile'
json: true
};
// async block
https.get(options, function(res) {
res.on('data', function(d) {
// just an example
profile.emails = [
{value: d[0].email }
];
});
}).on('error', function(e) {
console.error(e);
});
//sync operations that needs the result of the https call above
profile['who'] = "that's me"
// I should't save the profile until the async block is done
save(profile);
}
I was also trying to understand how to work with the Async library given that most of the node developers use this or a similar solution.
How can I "block" (or wait for the result) the flow of my script until I get the result from the http request? Possibly using the async library as an example
Thanks

Based on the fact that you're trying to "block" your script execution, I don't think you have a firm grasp on how async works. You should definitely read the dupe I suggested, especially this response:
How do I return the response from an asynchronous call?
To answer your question more specifically:
async.waterfall([
function(callback) {
// async block
https.get(options, function(res) {
res.on('data', function(d) {
// just an example
profile.emails = [
{value: d[0].email }
];
callback(null);
});
}).on('error', function(e) {
throw e;
});
},
function(callback) {
// I should't save the profile until the async block is done
save(profile);
callback(null);
}
]);

Related

Variable getting undefined outside the function [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 months ago.
I am trying to use the data from one api in another so i need to use the variable in another function call but it is getting undefined even after i've initialized it globally.
var latitude,longitude;
var cityn=req.body.cityname;
const appid="..."
var unit="metric"
const url1="https://api.openweathermap.org/geo/1.0/direct?q="+cityn+"&limit=5&appid="+appid
https.get(url1,function(response){
response.on("data",function(da){
const CityDetails=JSON.parse(da);
latitude=CityDetails[0].lat;
longitude=CityDetails[0].lon;
console.log(latitude) //--> defined
console.log(longitude) //--> defined
})
})
console.log(latitude) //--> undefined
console.log(longitude) //--> undefined
const url2="https://api.openweathermap.org/data/2.5/weather? lat="+latitude+"&lon="+longitude+"&appid="+appid+"&units="+unit;
I tried initializing it globally and also without using 'var' 'const' but both of the ways are not working.
This is because you want to log the lat-long data before it arrives from the server. You need to await the call before doing so or use synchronous HTTP request.
Better to use async-await for this kind of stuff. You can wrap the node.js HTTPS lib with this code:
function doRequest(options, data) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
res.setEncoding('utf8');
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
resolve(JSON.parse(responseBody));
});
});
req.on('error', (err) => {
reject(err);
});
req.write(data)
req.end();
});
}
https://stackoverflow.com/a/56122489/607033
You can use it this way:
var latitude,longitude;
var cityn=req.body.cityname;
const appid="..."
var unit="metric"
(async function (){
const options = {
hostname: 'api.openweathermap.org',
port: 443,
path: "/geo/1.0/direct?q="+cityn+"&limit=5&appid="+appid,
method: 'GET'
};
var CityDetails = await(doRequest(options));
latitude=CityDetails[0].lat;
longitude=CityDetails[0].lon;
console.log(latitude);
console.log(longitude);
const options2 = {
hostname: 'api.openweathermap.org',
port: 443,
path: "data/2.5/weather? lat="+latitude+"&lon="+longitude+"&appid="+appid+"&units="+unit,
method: 'GET'
};
var data2 = await(doRequest(options2));
})();

How to handle Gmail-API promises and returns? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm currently trying to do something with the Gmail-API but unfortunately I'm not able to create a getter method because the google functions are asynchronous.
So I basically have two functions:
function doSomething() {
let mailArray = [];
fs.readFile('credentials.json', (err, content) => {
mailArray = getBackupsMails(authorize(JSON.parse(content)));
});
// do something with mailArray
}
function getMails(auth) {
let mailArray = [];
gmail.users.messages.list({auth: auth, userId: 'me'}, function(err, response) {
let messagesArray = response.data.messages;
messagesArray.forEach((message) => {
gmail.users.messages.get({ auth: auth, userId: 'me', id: message.id }, function(err, response) {
message_raw = response.data.snippet;
text = message_raw.substring(16, 55);
mailArray.push(text);
});
});
});
return mailArray;
}
Unfortunately the mailArray is undefined. How can I get the filled array?
Since the API-call is asynchronous, you can only access the filled array once the API call has completed. Currently, return mailArray; gets executed before the API call is done.
To get the actual emails to the caller of getMails(), you will need to make getMails() asynchronous too - either with async/await, promises, or a callback.
As ponury-kostek said, see How do I return the response from an asynchronous call? for details.

Build an array of items in an async process [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I am a newbie in JavaScript and I am a bit stuck with the async way JS executes code...
Here is my code :
var marketPlaces = []
body.rows.forEach(function(doc) {
marketPlacesDB.get(doc.id, function(err, body){
if (err) {
callback(err, null)
} else {
console.log("Ajout d'une marketplace")
marketPlaces.push({id: body._id, name: body.name})
}
})
})
console.log("Retour des résultats")
callback(null, { marketPlaces: marketPlaces })
body.rows is an array containing ids of objects I would like to return in the marketPlaces array. For each element, I need to make a new request to the database to get the details of the objects (here only the "name").
The result is an empty array because the foreach loop ends before the callbacks of the get function return.
I can't figure out how to make this "synchronous".
Thanks for your answers.
Philippe.
If they didn't give you the synchronous API, you can't.
But you can still make it works 'synchronous' by adding a big callback. (I'm a non-native English speaker, dunno what word should I use here)
let counter = 0;
const max = marketPlacesDB.getLength(); // base on the real situation
function regularCallback() {
/* your callback */
if(++counter == max)
bigCallback();
};
function bigCallback() {
// continue on your original work
}
You can't make it synchronous if marketPlaceDb is not providing api. But you can make it work with asynchronous version too:
var async = require('async')
function getMarketPlaces(callback) {
var marketPlaces = []
async.eachSeries(body.rows, doc, function (next) {
marketPlacesDB.get(doc.id, function(err, body){
if (err) {
next(err, null) // finish async operation
} else {
marketPlaces.push({id: body._id, name: body.name})
next() // go next iteration
}
})
}, function (err) {
// eachSeries done
// here is marketPlaces
console.log("Retour des résultats")
callback(err, { marketPlaces: marketPlaces })
})
}
getMarketPlaces(console.log)
I used 'async' library from npm and eachSeries method to iterate array asynchronously.
Thanks to Ozgur using async library seems to be the most elegant way to answer my question.
The correct code is :
var marketPlaces = []
async.eachOfSeries(body.rows, function (item, key, next) {
marketPlacesDB.get(item.id, function(err, body){
if (err) {
next(err, null)
} else {
marketPlaces.push({id: body._id, name: body.name})
next()
}
})
}, function (err) {
console.log("Retour des résultats")
callback(err, { marketPlaces: marketPlaces })
})

Return JSON object from URL in Node.js / JavaScript [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I'm trying to create a function which returns a JSON object from an URL.
It should work like this:
function getObject(url) {
// return undefined when the url or json is invalid
return object;
}
So that I can use it in this way, if I would use the following URL:
var object = getObject('http://ip.jsontest.com/');
console.log('My IP is: ' + object.ip)
The JSON from the example looks like this:
{"ip": "127.0.0.1"}
So the code above should log this:
My IP is: 127.0.0.1
I already tried it with the request module and found this example on StackOverflow:
request.get({
url: 'http://ip.jsontest.com/',
json: true,
headers: {'User-Agent': 'request'} }, (err, res, data) => {
if (err) {
console.log('Error:', err)
} else if (res.statusCode !== 200) {
console.log('Status:', res.statusCode)
} else {
// data is already parsed as JSON:
console.log(data)
}
})
The data is displayed in the console as it should, but I found no way to use the it like in the example I provided above.
Is there a way to solve this without callbacks? I read that requests are asynchronous, but I need a synchronus solution.
No there is no way to do this synchronously. You could look into using the fetch api and an async function if they're supported in your environment.
For example:
async function getMyIpAndDoSomething() {
const response = await fetch("http://ip.jsontest.com/");
const object = await response.json();
console.log(object.ip);
}

Why doesn't my "required" function return anything? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
So i have this little Express.js Application, which accepts a POST-request with a "city" as body. The application processes the call and uses an external service for that. I tried to split up the "Pure" logic from the REST-controller to keep it clean, but somehow when i call the "weather" method, it won't return anything, even if the String, i am passing, is a valid object.
My guess is, that there is an issue with an asynchronious call, but i don't see myself in the position, to solve it myself.
RestController.js
module.exports = (function() {
var router = require('express').Router()
var bodyParser = require('body-parser')
var weather = require('./weather')
router.use(bodyParser.urlencoded({ extended: true }))
router.use(bodyParser.json())
//TODO DATABASE CONNECTION
//REST CONTROLLER
router.get('/', function(req, res) {
res.json({response: 'Hello world!'})
})
router.post('/weather', function(req, res) {
var r = weather(req.body.city)
res.send(r)
})
return router
})();
weather.js
var request = require('request')
module.exports = function(loc) {
request('http://api.openweathermap.org/data/2.5/weather?q='+loc+'&units=metric&APPID=API_TOKEN', function(err , ires) {
if(!err) {
json = JSON.stringify({temp: JSON.parse(ires.body).main.temp, name: JSON.parse(ires.body).name})
console.log(json)
return json
}
else {
return err
}
})
};
Best regards,
Tim
You should pass a callback to your weather function so you can handle it when it's complete. e.g.
module.exports = function(loc, callback) {
request('http://api.openweathermap.org/data/2.5/weather?q='+loc+'&units=metric&APPID=API_TOKEN', function(err , ires) {
if(!err) {
json = JSON.stringify({temp: JSON.parse(ires.body).main.temp, name: JSON.parse(ires.body).name})
console.log(json)
callback(null, json)
} else {
callback(err)
}
})
};
Then use as follows in your program:
router.post('/weather', function(req, res) {
var r = weather(req.body.city, function (e, result) {
if (e) return res.status(500).send({error: e});
res.send(result)
})
})
This is the standard node pattern (there are better ones). Node is non-blocking so it will not wait for your weather request to complete. It will eventually handle it on the event loop once the callback is finally invoked.

Categories