I am using google places api to get a list off cafes nearby. So,the api provides max 60 places but they are present in 3 different pages.To get the list of all 60 places, it provides tokens in the first and second page with the help of which we can access remaining places. I wrote a function that stores the place ids in an array.The places from first page is storing fine. But, in the first request i am accessing the token for the next page. The problem i am facing is, the request for second page is being called before assigning of the token.So, it results in an error. How can i make the reqeust for second page to wait until the token value is assigned?
const request= require('postman-request');
const nearbycafes = (latitude,longitude,callback) => {
var placeids=[];
let token;
let count=0;
const url='https://maps.googleapis.com/maps/api/place/nearbysearch/json?location='+latitude+','+longitude+'&radius=1500&type=cafe&key=xyz'
request({url: url, json: true},(error,response)=>{
console.log('ssss');
if(error){
callback('Error connecting to the API',undefined)
}
else if(response.body.results.length==0){
callback('No such address exist. Try something else',undefined)
}
else{
let i=0;
//for(i;i<response.body.results.length;i++){
placeids.push(response.body.results[0].place_id)
//}
if(response.body.next_page_token){
token=response.body.next_page_token;
count++;
console.log(count);
}
}
callback(undefined,{
placeids
})
})
console.log(count);
// if(count===1){
// const url2='https://maps.googleapis.com/maps/api/place/nearbysearch/json?location='+latitude+','+longitude+'&radius=1500&type=cafe&key=xyz='+token+''
// request({url: url2, json: true},(error,response)=>{
// console.log('ssss2');
// if(error){
// callback('Error connecting to the API',undefined)
// }
// else if(response.body.results.length==0){
// callback('No such address exist. Try something else',undefined)
// }
// else{
// let i=0;
// for(i;i<response.body.results.length;i++){
// placeids.push(response.body.results[i].place_id)
// }
// if(response.body.next_page_token){
// token=response.body.next_page_token;
// count++;
// }
// }
// callback(undefined,{
// placeids
// })
// })
// }
}
module.exports = nearbyhospitals;
You need to write a recursive function to get all the pages sequentially:
const request = require('postman-request')
function getPage (lat, long, token, cb) {
const url = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat},${long}&radius=1500&type=cafe&key=xyz${token ? `&token=${token}` : ''}`
request({ url: url, json: true }, (error, response) => {
if (error) {
cb(new Error('Error connecting to the API'), undefined)
} else if (response.body.results.length === 0) {
cb(null, [])
} else {
if (response.body.next_page_token) {
getPage(lat, long, response.body.next_page_token, (err, res) => {
if (err) {
return cb(err)
}
cb(undefined, res.concat(response.body.results))
})
} else {
cb(undefined, response.body.results)
}
}
})
}
function nearbycafes (latitude, longitude, callback) {
getPage(latitude, longitude, undefined, callback)
}
module.exports = nearbycafes
// just to try
nearbycafes(43.834527, 13.258358, (err, res) => {
console.log({ err, res })
})
Unfortunately, I cannot see the 'second' request in the code above. I assume the second request is being done in the 'callback' function. If you want to be sure that 'token' is not empty when 'callback' is executed, simply call the callback directly after assignment:
else{
let i=0;
//for(i;i<response.body.results.length;i++){
placeids.push(response.body.results[0].place_id)
//}
if(response.body.next_page_token){
token=response.body.next_page_token;
callback(undefined, { placeids: placeids });
count++;
console.log(count);
}
}
It appears that the positive (I assume) 'callback' call is outside of the 'else' clause. You sure you got the braces correct?
Best approach, though, is to avoid such 'global' variables as token in this case. You could parametrize callback and pass the token via function call. The rule of thumb in such chained requests is as follows:
function A(callback) {
$.ajax({
//...
success: function (result) {
//...
$.ajax({
// ...
success: function (result) {
// ...
}
})
}
});
}
Related
I'm new to node.js, and I'm trying to return a value from a function, but for some reason it's not working as planned.
In the code below, I want to return AuthToken from within the Try statement, but instead, it returns the values from outside the Try statement. If I delete that return line, then it just returns undefined. If I add return AuthToken instead then it also returns undefined - obviously because it hasn't completed the Try statement...
I've checked out a bunch of similar problems, but they haven't answered my problem.
Any suggestions?
"use strict";
const request = require('request'); // node package to create api request
var AuthToken;
/*
some stuff
*/
module.exports = {
FUNC1: (param1) => {
/*DO STUFF */
},
FUNC2: (param2) => {
/*DO STUFF*/
},
GetAuthToken: () => {
var options = {
/*OPTIONS*/
};
try {
request(options, (err, resp, body) => {
if (!err) {
let data = JSON.parse(body);
AuthToken = data["accessToken"];
return AuthToken; // <= I expect the AuthToken to be returned here.
} else {
console.log(err)
}
})
} catch (err) {
console.log(err)
}
return "Here instead"; //<= But the function is returning here instead before it's performed the request...
},
}
I eventually figured out that the request function is an asynchronous and so needs to be called with an async function.
I took the example code from here and modified it for my purposes.
I'm curious whether you can write if statements in an Express app to conditionally execute your code without providing else statements.
if(pred) {
doSomething()
}
return foo;
calcBar(); // doesn't run.
Above is the synchronous code that stops execution after the return statement.
My Express function looks like this:
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
if(data.length === 0) {
res.json({ message: "No data." });
}
const someOtherData = await someOtherGraphQLCall(data.foo);
res.json({ someOtherData });
} catch (err) {
res.json({err})
}
}
I know because of this question that code after the first res.json might still be executed. Is there a way to stop that? I don't want the second GraphQL call to execute if the first if condition is met. Is that possible without using else ?
Edit:
As the question I linked above mentioned, using a return statement is a bad option because:
it also makes it less meaningful and vague, cause it uses incorrect semantics. If you are not using the value from the function, then you shouldn't return one.
You can use return keyword on the first response to immediately return from the function.
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
if(data.length === 0) {
return res.json({ message: "No data." });
}
const someOtherData = await someOtherGraphQLCall(data.foo);
res.json({ someOtherData });
} catch (err) {
res.json({err})
}
}
Edit:
As an alternative, you can split the logic of the data and building up response. This way you can use return and it's easier to read:
app.get('/matches', async function (req, res) {
try {
const data = await getDataFromGraphQLCall();
res.json(data);
} catch (err) {
res.json({ err })
}
});
async function getDataFromGraphQLCall() {
const data = await someGraphQLCall();
if (data.length === 0) {
return { message: "No data." };
}
const someOtherData = await someOtherGraphQLCall(data.foo);
return { someOtherData };
}
If you are wondering if there is a way to achieve that without the else, yes it is.
But, It might not be THE cleanest way. IMO, using return is the best way to stop the execution of the controller.
Anyways, You can split the chunk of code into middlewares and use ternary operator to conditionally send responses.
In your example, separate out data = await someGraphQLCall(); as follows:
const middlewareOne = async function(req, res, next) {
let data = [];
let response = { message: "No data." };
try {
data = await someGraphQLCall();
req.locals.data = data; // <- attach the data to req.locals
} catch (err) {
response = { err };
}
data.length === 0 ? res.json(response) : next();
};
And then, mount the middlewareOne BEFORE your controller:
app.get("/matches", middlewareOne, async function controller(req, res) {
try {
const someOtherData = await someOtherGraphQLCall(req.locals.data.foo);
res.json({ someOtherData });
} catch (err) {
res.json({ err });
}
});
How this works is, the controller function would only be executed by express if the next() is called from the previous middleware -- middlewareOne in the example.
And as middlewareOne only calls next() if the data.length is not 0, it would work as you expected.
For more information on passing data from one middleware to other, read this
The return statement terminates the function execution in this context. In my opinion, you should handle the success case then the error case since the code will be read top to bottom.
In if statement, data could be undefined or null.
You can read more here: MDN - return
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
// alternative, if (data && data[0]) {
if (data && data.length) {
const someOtherData = await someOtherGraphQLCall(data.foo);
return res.json({ someOtherData });
}
return res.json({ message: "No data." });
} catch (err) {
console.log(err); // log error with logger and drain to loggly.
res.json({ err })
}
}
With Void operator:
Void operator allows you to return undefined but evaluate the given expression.
You can read more here: MDN - Void
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
// alternative, if (data && data[0]) {
if (data && data.length) {
const someOtherData = await someOtherGraphQLCall(data.foo);
return void res.json({ someOtherData });
}
return void res.json({ message: "No data." });
} catch (err) {
console.log(err); // log error with logger and drain to loggly.
res.json({ err })
}
}
I have an array of items that I need to post to my server. I've tried the following but i never iterates.
var i = 0;
while (i < numOfItems) {
var item = items[i];
var a;
for(var ik in item){
console.log(item[ik]);
a = item[ik]; // Gets the key
break;
}
var formData = {
ID : ID,
UID : UID,
item : a
}
request.post({url:'http://example.com/a', formData: formData}, function(err, httpResponse, body){
if (err) {
return console.error('Post failed:', err);
}
console.log('Post successful! Server responded with:', body);
i++;
});
}
Your code won't work because request.post is asynchronous. If your objective is to make a call for each element in the array, a working and a more elegant solution would be to use Promises.all().
Here's your code modified with Promises —
function postRequest(url, formData) {
return new Promise((resolve, reject) => {
request.post({ url, formData }, function (err, httpResponse, body) {
if (!error) {
resolve({ message: 'Post successful!', response: body });
} else {
reject(err);
}
});
})
}
// Map your data array to an array of Promises
let promises = yourArray.map(element => {
let formData = {
ID: ID,
UID: UID,
item: element
}
return postRequest({ url: 'http://example.com/a', formData: formData })
});
// Wait for all Promises to complete
Promise.all(promises)
.then(results => {
// Handle results
})
.catch(e => {
// Handle error
});
A few things to note -
I'm reusing the fields ID and UID as-is, as it isn't clear where they come from in your code.
Replace yourArray with the array containing your data items.
I'd like to perform queries in order but I don't know what is the best approach.
Let's say I'd like to do the following :
if (name) {
//first query
db.query({name:name}).exec(function(err,result) {
//save result
})
}
if (isEmpty(result)) {
//make another query
db.query({anotherField:value}).exec(function(err,result) {
//save result
})
}
Should I use promises on that case?
That would be an example with cakePHP :
if (!isset($field1)) {
$result = $this->item->find( ... conditions => ... = $field2);
} else {
if (!isset($field2)) {
$result = $this->item->find( ... conditions => ... = $field1);
} else {
$result = $this->item->find( ... conditions => ... = $field1 && ... =$field2);
if (empty($result)) {
$result = $this->item->find( ... conditions => ... =$field2);
}
}
}
If you mean "in order" you can nest the callbacks. Passing callbacks is the classic (non-promise) way to structure asynchronous code:
function doMultipleAsyncThings(name, callback){
if (name) {
//first query
db.query({name:name}).exec(function(err,result) {
if (isEmpty(result)) {
//make another query
db.query({anotherField:value}).exec(function(err,result) {
//save result
})
} else {
//save result
}
})
} else {
return callback('no name');
}
}
Heads up, after more than 2 or so operations, you end up in 'callback hell' with 100+ lines of nested code, the async library is helpful for this:
var async = require('async');
doMultipleAsyncThings('plato', function(){
console.log(arguments)
});
function doMultipleAsyncThings(name, callback){
// `callback` is a passed-in function to call after doMultipleAsyncThings is done
// Here, it is the function we passed in above after 'plato'
async.waterfall([function(done){
done(null, name);
},
firstQuery,
secondQuery,
], callback)
}
function firstQuery(name, done){
if (name) {
// You can define and pass a callback inline:
db.query({name:name}).exec(function(err,result) {
done(err, result);
})
} else {
done('no name');
}
}
function secondQuery(result, done){
if (isEmpty(result)) {
// You can pass a callback by reference:
db.query({anotherField:value}).exec(done)
} else {
//save result
done();
}
}
Promises would be a good fit for this, the q library is the most common for this. You probably just want to nest your promises like so:
var q = require('q');
if (name) {
//first query
q.ninvoke(db.query({name:name}), 'exec')
.then(function(result) {
//save result
if (isEmpty(result)) {
//make another query
q.ninvoke(db.query({anotherField:value}), 'exec')
.then(function(result) {
//save result
})
.fail(function(err) {
//handle failure
console.error(err, err.stack);
});
}
})
.fail(function(err) {
//handle failure
console.error(err, err.stack);
});
}
q.ninvoke allows us to convert standard nodejs functions into their promise equivalent.
I have a script in nodeJS that connects to my postgres DB, the issue is that on a callback method I'm initiating a variable but it seems always empty, this my app.js script :
var ck_username = /^[A-Za-z0-9_]{1,20}$/;
function fncCheckUsernameAvailability(vstrUsername, callback) {
var pg = require("pg");
var client = new pg.Client({user: 'xxx', password: 'xxx', database: 'xxx', host: 'xxx.com'});
client.connect(function(err) {
if (err) {
callback(err, null);
return;
}
client.query("SELECT username FROM users WHERE username ='"+vstrUsername+"'", function(err, result) {
if (err) {
callback(err, null);
return;
}
if (result.rows[0] == undefined) {
callback(null, null);
}else {
callback(null, 'Username already taken');
}
client.end();
});
});
}
app.post("/Signup", function(req, res){
var username = req.body.username;
var usernameError = '';
var errors = [];
if (!ck_username.test(username)) {
errors[errors.length] = "Invalid username";
usernameError = "Invalid username";
}
if (ck_username.test(username)) {
//The issue starts here
fncCheckUsernameAvailability(username, function(err, result) {
if(result != null){
errors[errors.length] = result;
usernameError = result; // usernameError is always empty
console.log(result);//This is printed in console, I can see the result
}
});
}
if (errors.length > 0) {
res.render('Signup.ejs', {
layout:false,
usernameError:usernameError // usernameError is sent to the view empty
});
}
else{
res.render('Signup.ejs', {
layout:false,
usernameError:'No errors'
});
}
});
The result is displayed on cansole so Can someone tell me why this variable is not instantiated , is this callback an asynchronous one?
is this callback an asynchronous one?
Yes.
//This line executes at time 1
if (ck_username.test(username)) {
//The issue starts here
fncCheckUsernameAvailability(username, function(err, result) {
//this section of code executes at time 3! Oh snap async blew my mind!
if(result != null){
errors[errors.length] = result;
usernameError = result; // usernameError is always empty
console.log(result);//This is printed in console, I can see the result
}
});
}
//This section of code executes at time 2 which is BEFORE time 3
if (errors.length > 0) {
res.render('Signup.ejs', {
layout:false,
usernameError:usernameError // usernameError is sent to the view empty
});
}
You have to move ALL the code that requires the result variable INSIDE that callback function. Putting it outside the function but lower in the file makes things execute in the wrong order.
Also checkout http://callbackhell.com for guidance on adjusting to code organization in an async environment.
Yes, the callback is asynchronous, which can execute at any time. It is probably then not yet defined at the time you are accessing. Therefore, you should access the variable from inside the callback, so it will always be defined at that point.
app.post('/Signup', function (req, res) {
var username = req.body.username;
if (!ck_username.test(username)) {
res.render('Signup.ejs', {
layout: false,
usernameError: 'Invalid username'
});
} else {
fncCheckUsernameAvailability(username, function (err, result) {
if (result != null) {
res.render('Signup.ejs', {
layout: false,
usernameError: result
});
} else {
res.render('Signup.ejs', {
layout: false,
usernameError: 'No errors'
});
}
});
}
});
Here is an example of how callbacks work, and why your variable didn't return the expected value. Take these two functions:
var foo = function(num, callback) {
setTimeout(function() {
callback(num * 2);
}, 5000);
};
This function multiplies the number by two, but after 5 seconds. If you try to access the variable before those 5 seconds, you don't have a value, like this case:
var num = 0;
foo(5, function(result) {
num = result;
});
console.log(num); // returns 0
In this case, num is only set to 10 after 5 seconds, console.log() will have executed before that time has passed.