Because of the complexity of this application, I have a need to wrap Facebook API calls, like so.
//In main file, read is always undefined
var read = fb_connect.readStream();
// In fb_wrapper.js
function readStream () {
var stream;
FB.api('/me/feed', {limit:10000}, function (response) {
stream = response.data;
});
return stream;
}
I know that due to the asynchronous nature of the call, the rest of the readStream() function will return stream (which has no value). I am having trouble finding a way of getting the data out of the callback function scope and back up to a higher scope. The FB API call is returning fine (I have debugged it a hundred times), but getting that response data has been the battle thus far.
If anyone has any suggestions, it would be much appreciated. I searched for Facebook jQuery plug-ins (as a pre-made wrapper, perhaps) with little luck.
Judging from your question, it seems that you are looking for a synchronous call. Which means that you'd want to use the data returned from the api call right after calling it. In that case, you'll need to check whether FB.api supports synchronous calls (mostly doesn't).
Otherwise, you'll need to understand that you are making an async call here. Which means that you should put your handling code INSIDE the callback function that you pass to FB.api. This is called the "continuation" style of writing code and is the standard way to use async calls.
FB.api('/me/feed', {limit:10000}, function (response) {
var stream = response.data;
// Do your processing here, not outside!!!
});
Or:
function handlerFunction(response) {
// Do your processing here
}
FB.api('/me/feed', {limit:10000}, handlerFunction);
Related
I concede that, despite hours of reading and attempting, I am fundamentally unable to grasp something about Deferred promises and asynchrony in general.
The goal on my end is real, real simple: send some data to the server, and react to the contents of the response conditionally.
The response will always be a JSON object with save and error keys:
{ "save": true, "error":false}
// or
{ "save" : false,
"error" : "The server has run off again; authorities have been notifed."}
I have tried dozens and dozens of variations from the jQuery API, from other stackexchange answers, from tutorials, etc.. The examples all seem concerned with local asynchronous activity. When I need is some ability to be made aware when the AJAX request has either finished and returned a response I can inspect and make decisions about, or else to know that it's failed. Below, I've used comments to explain what I think is happening so someone can show me where I'm failing.
I know this is a repost; I am, apprently, worse than on average at grasping this.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
/* when(), as I understand it, should fire an event to be
responded to by then() when it's contents have run their course */
var result = $.when(
/* here I believe I'm supposed to assert what must complete
before the when() event has fired and before any chained
functions are subsequently called */
/* this should return a jqXHR object to then(), which is,
I'd thought, a queue of functions to call, in order,
UPON COMPLETION of the asynchronous bit */
$.post("my/restful/url", postData))
.then( function() {
/* since "this" is the jqXHR object generated in the $.post()
call above, and since it's supposed to be completed by now,
it's data key should be populated by the server's response—right? */
return this.data;
});
// alas, it isn't
console.log(result.data);
// >> undefined
Most examples I can find discuss a timeout function; but this seems, as I understand, to be a failsafe put in place to arbitrarily decide when the asynchronous part is said to have failed, rather than a means of stalling for time so the request can complete. Indeed, if all we can do is just wait it out, how's that any different from a synchronous request?
I'll even take links to a new read-mes, tutorials, etc. if they cover the material in a different way, use something other than modified examples from the jQuery API, or otherwise help this drooling idiot through the asynchronous mirk; here's where I've been reading to date:
jQuery API: Deferred
JQuery Fundamentals
jQuery Deferreds promises asynchronous bliss (blog)
StackOverflow: timeout for function (jQuery)
Update
This is in response to #Kevin B below:
I tried this:
var moduleA = {
var moduleB = {
postData: {"id":7, "answer":"Ever since I went to Disneyland..."};
save: function() {
return $.post("path/to/service", postData, null, "JSON");
}
};
var result = this.moduleB.save();
result.done(function(resp) {
if (resp.saved == true) {
// never reached before completion
console.log("yahoo");
} else {
console.log("Error: " + resp.error);
// >> undefined
}
});
}
You are over-complicating your code. You cannot get the data to outside of the callback, no matter how many deferred/promises you create/use (your sample creates 3 different deferred objects!)
Use the done callback.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
$.post("my/restful/url", postData).done(function (result) {
console.log(result.save, result.error);
});
You seem to have a misunderstanding of both asynchronous requests, the Promise pattern, and Javascripts mechanism of passing functions as an argument.
To understand what's really happening in your code I suggest you use a debugger and set some breakpoints in the code. Or, alternatively, add some console.logs in your code. This way you can see the flow of the program and might understand it better. Also be sure to log the arguments of the function you pass as an argument in the then()-method, so you understand what is passed.
ok you got it half right. the problem is that when you execute the console.log the promised is not yet fulfilled the asynchronous nature of the promises allows the code to execute before that ajax operation is done. also result is a deferred not a value, you need to handle your promised with .done instead of .then if you wish to return a value otherwise you'll continue passing promises.
so that said
var result={};
$.when(
$.post("my/restful/url", postData))
.done( function(data) {
result.data=data;
});
// here result is an object and data is a undefined since the promised has no yet been resolve.
console.log(result.data);
I am quite new (just started this week) to Node.js and there is a fundamental piece that I am having trouble understanding. I have a helper function which makes a MySQL database call to get a bit of information. I then use a callback function to get that data back to the caller which works fine but when I want to use that data outside of that callback I run into trouble. Here is the code:
/** Helper Function **/
function getCompanyId(token, callback) {
var query = db.query('SELECT * FROM companies WHERE token = ?', token, function(err, result) {
var count = Object.keys(result).length;
if(count == 0) {
return;
} else {
callback(null, result[0].api_id);
}
});
}
/*** Function which uses the data from the helper function ***/
api.post('/alert', function(request, response) {
var data = JSON.parse(request.body.data);
var token = data.token;
getCompanyId(token, function(err, result) {
// this works
console.log(result);
});
// the problem is that I need result here so that I can use it else where in this function.
});
As you can see I have access to the return value from getCompanyId() so long as I stay within the scope of the callback but I need to use that value outside of the callback. I was able to get around this in another function by just sticking all the logic inside of that callback but that will not work in this case. Any insight on how to better structure this would be most appreciated. I am really enjoying Node.js thus far but obviously I have a lot of learning to do.
Short answer - you can't do that without violating the asynchronous nature of Node.js.
Think about the consequences of trying to access result outside of your callback - if you need to use that value, and the callback hasn't run yet, what will you do? You can't sleep and wait for the value to be set - that is incompatible with Node's single threaded, event-driven design. Your entire program would have to stop executing whilst waiting for the callback to run.
Any code that depends on result should be inside the getCompanyId callback:
api.post('/alert', function(request, response) {
var data = JSON.parse(request.body.data);
var token = data.token;
getCompanyId(token, function(err, result) {
//Any logic that depends on result has to be nested in here
});
});
One of the hardest parts about learning Node.js (and async programming is general) is learning to think asynchronously. It can be difficult at first but it is worth persisting. You can try to fight and code procedurally, but it will inevitably result in unmaintainable, convoluted code.
If you don't like the idea of multiple nested callbacks, you can look into promises, which let you chain methods together instead of nesting them. This article is a good introduction to Q, one implementation of promises.
If you are concerned about having everything crammed inside the callback function, you can always name the function, move it out, and then pass the function as the callback:
getCompanyId(token, doSomethingAfter); // Pass the function in
function doSomethingAfter(err, result) {
// Code here
}
My "aha" moment came when I began thinking of these as "fire and forget" methods. Don't look for return values coming back from the methods, because they don't come back. The calling code should move on, or just end. Yes, it feels weird.
As #joews says, you have to put everything depending on that value inside the callback(s).
This often requires you passing down an extra parameter(s). For example, if you are doing a typical HTTP request/response, plan on sending the response down every step along the callback chain. The final callback will (hopefully) set data in the response, or set an error code, and then send it back to the user.
If you want to avoid callback smells you need to use Node's Event Emitter Class like so:
at top of file require event module -
var emitter = require('events').EventEmitter();
then in your callback:
api.post('/alert', function(request, response) {
var data = JSON.parse(request.body.data);
var token = data.token;
getCompanyId(token, function(err, result) {
// this works
console.log(result);
emitter.emit('company:id:returned', result);
});
// the problem is that I need result here so that I can use it else where in this function.
});
then after your function you can use the on method anywhere like so:
getCompanyId(token, function(err, result) {
// this works
console.log(result);
emitter.emit('company:id:returned', result);
});
// the problem is that I need result here so that I can use it else where in this function.
emitter.on('company:id:returned', function(results) {
// do what you need with results
});
just be careful to set up good namespacing conventions for your events so you don't get a mess of on events and also you should watch the number of listeners you attach, here is a good link for reference:
http://www.sitepoint.com/nodejs-events-and-eventemitter/
I'm writing tests for my Node.js/Express/Mongoose project using Mocha and Should.js, and I'm testing out my functions that access my MongoDB. I'm want these tests to be completely independent from the actual records in my database, so I want to create an entry and then load it, and do all my tests on it, then delete it. I have my actual functions written (I'm writing tests after the entire project is complete) such that the create function does not have a callback; it simply just renders a page when it's done. In my tests script, I call my load_entry function after I call create, but sometimes create takes longer than usual and thus load_entry throws an error when it cannot actually load the article since it has yet to be created. Is there any way to make sure an asynchronous function is finished without using callbacks?
Please let me know if there is any more info I can provide. I looked all over Google and couldn't find anything that really answered my question, since most solutions just say "use a callback!"
Use what is known as a promise
You can read more about it here.
There are lots of great libraries that can do this for you.
Q.js is one I personally like and it's widely used nowadays. Promises also exist in jQuery among many others.
Here's an example of using a q promise with an asynchronous json-p call: DEMO
var time;
$.ajax({
dataType: 'jsonp',
type: 'GET',
url: "http://www.timeapi.org/utc/now.json",
success: function (data) {
time = data;
},
error: function (data) {
console.log("failed");
}
})
.then(function(){ // use a promise library to make sure we synchronize off the jsonp
console.log(time);
});
This is definitely the kind of thing you want a callback for. Barring that, you're going to have to write some kind of callback wrapper that polls the database to determine when it has finished creating the relevant records, and then emits an event or does some other async thing to allow the test to continue.
Since the only native way to do asynchronous things are: setTimeout, setInterval and addEventListener, and they all take a callback you will eventually have to use a callback somewhere.
However, you can hide that by using Promises/A, also known as Deferreds.
Your code could look like this:
db.create_entry("foo", data).done(function (entry) {
db.delete_entry(entry).done(function () {
console.log("entry is deleted");
});
});
Using then-chaining:
db.create_entry("foo", data).then(function (entry) {
return db.delete_entry(entry);
}).done(function () {
console.log("entry is deleted");
});;
I found a solution that works. What I did was to add a callback to my function (next) and only call it if it's specified (i.e., for the tests):
//more stuff above
article.save(function(err){
if (!err) {
console.log(req.user.username + ' wrote ' + article.slug)
return next() || res.redirect('/admin')
}
return next(err) || res.render('articles/entry_form', {
title: 'New Entry',
article: article,
})
})
This way, when I run the actual server and no callback is specified, it won't throw an error, because it will simply return the res.render statement.
With qUnit, I understand how to use asyncTest() if you have asynchronous code within your tests, but what if you have a function that contains asynchronous code?
In other words, the asynchronous request is not within the test, but is simply part of the code that is being tested.
Take this code for example:
function makeAjaxCall(){
$.get('/mypage', {}, function(data){
// Do something with `data`
},'json');
}
How can I call makeAjaxCall() within a test and then run tests on the data that is returned from the ajax request?
You could use the jQuery Global Ajax Event Handlers in this situation. Bind it before calling, unbind it once you finish the test, possibly via the module method in Qunit.
Something like (untested):
asyncTest(function() {
expect(1);
$(document).ajaxSuccess(function(e, xhr, settings) {
var jsonRep = JSON.parse(xhr.responseText);
// assert on jsonRep
//ok(true);
});
$(document).ajaxError(function(e) {
ok(false);
});
$(document).ajaxComplete(function() {
start();
$(document).unbind('ajaxSuccess ajaxError ajaxComplete');
});
makeAjaxCall();
});
Note that the global handlers do not have access to the parsed contents, and you have to reparse them yourself using JSON.parse. Not ideal, but I don't know of another way.
It seems that nobody cares about mockAjax, so I found something better these days: sinonJS !
Do you know the feature autoResponse of this wonderful tool named Fiddler?
sinonJS allows you to do the same on the client side, it's a kind of ajax by-pass proxy. if you are interested to see an example, let me know... so sinonJS can respond to any ajax request without accessing the server, that's kind of magic if you want to mock your client, if you want to unit test your javascript code.
I am not having any issues with logging in or even calling the api, I just have an issue with getting the response outside of the api callback. I know that it runs asynchronously so I would like to put it in a function that would return the response. Here is my idea
//What I would like to be able to do
function fbUser(){
FB.api('/me', function(response){
//this logs the correct object
console.log(response);
});
//How do I get the response out here?
return response;
}
I would like to call the /me api function once in the beginning and then pass it around to my view objects (I just use the response inside of Backbone Views) and depending on what is needed make other api calls. I currently have certain things working by calling the view from inside of the callback
//What I am doing now, but I lose the ability to pass anything other than the
//the current response to this function/View
FB.api('/me', function(response){
var newView = new facebookView({model: response});
});
I orginally was trying this, but because the api call is asynchronous I had issues with things being undefined
//What I started with but had async issues
var fbResponse;
FB.api('/me', function(response){
fbResponse = response;
});
//I would then try and use fbResponse but it would be undefined
I lose the first response when I make the second. For example my first api call is to /me to get the user info. I can then call /your-fb-id/photos and get photos, but if I make the call to another function inside of the photo api callback I only can reference that response I lost the original /me response. If I could get the response out of the callback then I would be able to pass it as needed. I understand that response is only valid inside of the callback, so how do I make it valid outside of the callback while taking into account it's asynchronousness?
OK everyone I figured this out. It took me a long time and reading many different pages. What I said I wanted to do all deals with callbacks and closures. I will first cover the callback issue. Because the FB.api function is asynchronous you never know when it will return. You can stop Javascript or set a timer, but that is a horrible way to do this. You need a callback. In fact the FB.api is using a callback. That's what the anonymous function as the second parameter is. What I did was create another function that called fbUser and used a callback. Here is what I did:
function startThis() {
var getUser = fbUser(function(model){
console.log(model);
startapp(model);
});
};
function fbUser(callback){
FB.api('/me', function(response){
callback(response);
});
}
The startThis function is called on a positive Facebook auth response. It then calls the fbUser function which has a callback. The callback returns the callback from the FB.api function. Because startThis uses that callback with the return value being called model, the other code will not execute until the callback returns. No more undefined issues. These functions are just wrappers to get the Facebook response to my views. I may have added one too many layers of abstraction, but if you want to pass around the response this is the way.
Secondly I wanted to pass this response on to another view. For example one view loads basic info (using the response from fbUser). I now want to pass that to another view that loads photos (I know this is not best practices MVC, but by using Facebook I don't have much control over the Model). The problem I had though was I couldn't pass the original response to the next view because inside of the callback function for the FB.api call this refers to the Window and not the object I was in. Solution: closures. I won't explain this perfectly, but a closure is a local variable inside of a function that still has a reference inside of an anonymous function. Here is my solution which should illustrate what I am talking about:
photos: function(){
var This = this;
var apiString = '/' + this.model.id + '/photos';
FB.api(apiString, function(response){
loadPhoto(response, 1, This.model);
});
The function loadPhoto is a wrapper to load a photo view (I know backbone can help me with loading different views, but I was tackling one problem at a time). It takes the photo api call as the model, a number as an offset, and the origanl response. The first line in this function sets this to a local variable This. That allows me inside of the anonymous callback function to reference the object this was called from.
I hope this can help someone as I spent a lot of hours and a lot of testing time to find the solution to this. If you don't know about how callbacks or closures work it is hard to find the information you are looking for.