On May 5, 2014, at 17:47, Folivi Fofo wrote:
> I'm working on an app where I need to display user's facebook events.
>
> The code below shows how to retrieve the users events and then perform a query to facebook api to retrieve more event details.
>
> As you can see, the process is: retrieving the events, then get more details about each, push all into an array then pass the array to the view.
>
> I fail in sending the array to the view and I believe it might have something to do with the asynchronous way of nodejs. Is this where the callback is needed?
>
> How can I solve this?
>
> Hope I'm clear.
>
> Thanks for your help
>
> index : function(req, res){
> graph.setAccessToken(req.user.token);
> var events = [];
> graph.get("/me/events?limit=500", {limit: 500}, function(err, rep) {
Here you are calling the graph.get function, and passing it a callback (the function(err, rep) {…} function) and telling it you would like it to call that function when it has finished getting the graph.
> for(key in rep["data"]){
> var query = "SELECT eid, name, creator, description, pic, pic_big, pic_cover, start_time, end_time FROM event WHERE eid=" + rep["data"][key].id;
> graph.fql(query, function(err, event_hash) {
And here, for each item returned from graph.get, you are calling graph.fql, and again telling it to call a callback (function(err, event_hash) {…}) when it is complete.
> var ev = event_hash["data"][0];
> if (ev.creator.toString() == req.user.uid.toString()) {
> events.push(ev);
> };
> })
> };
> });
> res.view({
> events: events
> });
Here you are responding with the view, right after having called graph.get, which is before graph.get has completed, which is before you call graph.fql, which is before graph.fql completes, which is before the callbacks have run, which is before you have pushed things onto the events array. So your view is rendered with an empty array.
You need to wait until all the callbacks have finished before calling res.view.
One way to do this is to keep a counter of how many tasks you need to complete, and decrement it when each task finishes; when the counter reaches 0, you can render the view.
index : function(req, res){
graph.setAccessToken(req.user.token);
var events = [];
graph.get("/me/events?limit=500", {limit: 500}, function(err, rep) {
var tasks = 0;
for(key in rep["data"]){
++tasks;
var query = "SELECT eid, name, creator, description, pic, pic_big, pic_cover, start_time, end_time FROM event WHERE eid=" + rep["data"][key].id;
graph.fql(query, function(err, event_hash) {
var ev = event_hash["data"][0];
if (ev.creator.toString() == req.user.uid.toString()) {
events.push(ev);
};
if (--tasks == 0) {
res.view({
events: events
});
}
})
};
});
},
However, the above assumes no error will ever occur, which is not likely. You need to consider what should happen when an error occurs 1) from graph.get(); 2) from any of the graph.fql() calls. Do you wish to call the view with an error object instead of the events object? If so, then you need to write code to do that, everywhere an error could occur (i.e. in every callback that’s given an err parameter).
There are libraries (npm modules) that can help you manage your control flow, if you don’t want to do it manually as described above. async is a popular one but there are many others. Consider taking some hours to play with various control flow libraries to see how they work and evaluate if any of them makes enough sense to you to warrant using it in your project.