UPDATE
Got it to work, but I still think there's a problem. I only get the correct return data if I set the setTimeout timer to be really long, like 2000. If I leave it at 200 then the callback function executes with empty data because the API call hasn't been returned yet.
I've updated the code below.
Setup:
I'm sending a get value from the front end via AJAX (jQuery) and then using that value to call the Foursqaure API to get a list of relevant venues back.
This is working "fine" except that the order of events get screwy. When I insert the GET value into the parameters for the function to evaluate, I'm getting a return value that I'm not asking for, which then causes the template to render on the front end before the other return value from my function – the one I want – is given.
And actually I don't think it's actually being returned. Just logged to the console.
Question:
How can I return the filtered list of JSON objects at the end of the initGetVenues function in places.js via AJAX to the front end?
Context:
I'm using this package to connect to Foursquare: https://npmjs.org/package/foursquarevenues
$("#search-items").submit(function() {
var placeQuery = $("#search-input").val();
$.ajax({
url: '/return/places/',
data: {"passPlaceQuery": placeQuery},
type: 'get',
dataType: 'html',
success: function(data) {
$("#search-results-list").html(data);
},
});
return false;
});
returnPlaces: function(req, res) {
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
console.log("I've started routing");
return places.findVenue({
ll: "38.214986,-85.637054",
radius: 32186,
query: req.query.passPlaceQuery,
intent: "browse",
categoryId: "4bf58dd8d48988d1e0931735"
}, function(err, data) {
console.log("Venue callback");
if (err) {
res.send(500);
}
console.log("Attempting render: " + data);
return res.render("place-results", {
layout: false,
foundPlaces: data
});
});
} else {
return res.redirect("/");
}
}
(function() {
var foursquare, initGetVenues;
foursquare = (require('foursquarevenues'))('SECRET', 'SECRET');
module.exports = {
findVenue: initGetVenues = function(criteria, callback) {
var jsonUniquePlaces;
jsonUniquePlaces = [];
foursquare.getVenues(criteria, function(error, venues) {
var i, objUniquePlace, range, uniquePlaces, venueName;
if (!error) {
range = Object.keys(venues.response.venues).length;
uniquePlaces = [];
i = 0;
while (i < range) {
venueName = venues.response.venues[i].name;
if (!(uniquePlaces.indexOf(venueName) > -1)) {
uniquePlaces.push(venueName);
}
i++;
}
i = 0;
while (i < uniquePlaces.length) {
objUniquePlace = {
place: uniquePlaces[i]
};
jsonUniquePlaces.push(objUniquePlace);
i++;
}
jsonUniquePlaces = JSON.stringify(jsonUniquePlaces);
return jsonUniquePlaces;
}
});
return setTimeout((function() {
return callback(null, jsonUniquePlaces);
}), 200);
}
};
}).call(this);
When setTimeout is 2000 I get:
| I've started routing
| [{"place":"Quills Coffee"},{"place":"Quills Coffe"},{"place":"Quill's Coffee"}]
| Venue callback
| Attempting render: [{"place":"Quills Coffee"},{"place":"Quills Coffe"},{"place":"Quill's Coffee"}]
| GET /return/places/?passPlaceQuery=quills 200 2009ms - 150
When setTimeout is 200 I get:
| I've started routing
| Venue callback
| Attempting render:
| GET /return/places/?passPlaceQuery=quills 200 210ms - 11
| [{"place":"Quills Coffee"},{"place":"Quills Coffe"},{"place":"Quill's Coffee"}]
You can't just return your value from inside findVenue. The call to foursquare.getVenues is asynchronous. So when the node engine gets to the function call foursquare.getVenues(opt, callback) it simply starts the operation and continues executing any further statements, then index.js continues executing, then you render a response... Finally some time later, the foursquare.getVenues code invokes its callback (presumably when it's done talking to the foursquare API.)
You need to rewrite places.findVenue to take a callback argument. When you call places.findVenue() you will pass it a function to execute as a callback. That is when you're supposed to send the response.
Here is a simplified example that you can hopefully extend:
function findVenue(opt, callback){
setTimeout(function(){
console.log('Done with foursquare.getVenues, calling back to routing function')
callback(null, opt);
// you passs this callback function to setTimeout. it executes it in 200ms
// by convention, pass a null error object if successful
}
,200);
};
app.get('/return/places', function(req, res){
console.log('routing function start');
findVenue({
lat:40,
lng: 70,
query: 'foo'
}, function(err, data){
console.log('findVenue callback');
if(err){ return res.send(500) };
res.render('template', {foo: data});
});
});