Handling variable assignment using callbacks in Javascript? [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
What is the difference between synchronous and asynchronous programming (in node.js)
(10 answers)
Closed 6 years ago.
I've recently picked up Node and see that things don't always run sequentially. I'm pretty confused as I'm used to
1) Assignment
2) Print data
Current I'm running the below function and calling var x = searchForProfessor("prof_name_here");
I then call console.log(x); only to get undefined.
I've been reading about callbacks all over the web and I can't wrap my head around the idea and apply it to this code. Can someone give me some intuition into making the above possible with callbacks?
My Function
var searchForProfessor = function searchForProfessor(teacher_name) {
google.resultsPerPage = 10
var nextCounter = 0
google(teacher_name, function (err, res){
for (var i = 0; i < res.links.length; ++i) {
var link = res.links[i];
if (!link.title.includes('Add') || !link.title.includes('RATINGS') || !link.title.includes("Hint")) {
request(link, function(err, resp, body){
if (!err && resp.statusCode == 200) { //If no error is going to happen, then print the data
var $ = cheerio.load(body); //Grab the body of data from 'prof_link'
var overall_rating = $('.breakdown-header .grade').text(); //Get the grade rating from the following classifications text
if (overall_rating.substr(0,3)) {
teacher_results.push(prof_name);
} //End if
} //End if
}); //End request
}//End if for comparisons ||
} //End For
}); //End google function
} //End searchForProfessor

Because both your google and request functions are asynchronous data returned from those functions will not be immediately available, which is why x
var x = searchForProfessor("prof_name_here");
will always give you undefined when you try to log it.
To mitigate this problem you can pass in functions as parameters to other functions to return that data when it's available.
Here's a small mockup of what's happening with your code complete with how callbacks are used.
async1 mimics a database connection - it returns an object using a key after 1 second by calling the function that's passed into it with the data as its first argument.
function async1(name, callback) {
var obj = { tea001: { first: 'Dave', last: 'Batman' } };
setTimeout(function () {
callback(obj[name]);
}, 1000);
}
Here we have your searchForProfessor function which accepts a name and a function. It calls async1 with the name, passing in the callback as the second parameter. Note the data which is returned is the returned object data from async1. The callback that was passed into searchForProfessor is used to return that data to...
function searchForProfessor(teacher_name, callback) {
async1(teacher_name, function (data) {
callback(data);
});
}
...this function call to searchForProfessor at which point you can log the data.
searchForProfessor('tea001', function (data) {
console.log(data);
});
You can see in the demo that the returned data takes around a second to be logged to the console once the code is run.
DEMO

Related

JQuery.when() isn't waiting for a function return [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 2 years ago.
I have the following code structure :
var firstPagePromise = $.ajax('url1').then(function (data) {
//do stuff
});
var secondPagePromise = $.ajax('url2').then(function (data) {
//do stuff
});
$.when(firstPagePromise, secondPagePromise).done(function () {
// do stuff
});
I want to execute some code after doing two request using JQuery.when()
. Written like this, it's working, but I would like to put the two requests in functions for easier maintenance of the code and I'm unsure how to do that.
function fetchFirstPage() {
var firstPagePromise = $.ajax('url1').then(function (data) {
//do stuff
});
return firstPagePromise;
}
var firstPagePromise = fetchFirstPage();
// same for the second page ...
$.when(firstPagePromise, secondPagePromise).done(function () {
// do stuff
});
this does not work and seems to fire when() without waiting for the execution of the two functions, is there any way I could execute the code in the when function only if the two previous function have ended ?
The following code shows my issue on a minimal example, I'd like to get the variable title to print in the console after the call to when but I get "undefined" instead. I think that I understand what's going on, inside the function fetchPage(), title is returned before the request finishes. I think that a callback function could resolve the issue, however, is there any other way ?
function fetchPage() { //fetch the random start page
var title;
var pagePromise = $.ajax('https://en.wikipedia.org/w/api.php?action=query&origin=*&format=json&list=random&continue=-||&rnnamespace=0&rnlimit=1').then(function (data) {
title = data.query.random[0].title;
console.log("in request : " + title);
});
console.log("in function return : " + title);
return [title,pagePromise];
}
var firstPageResult = fetchPage();
var title = firstPageResult[0];
var pagePromise = firstPageResult[1];
$.when(pagePromise).done(function() {
console.log("in when : " + title);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>

What is wrong with this js code, Returning variable from function [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 working on a project where i post "thumbsup" and "like" click via AJAX/POST on a PHP Script. This returns after processing, depending if the user is logged in or not an error array (with json_encode). 0 means its all ok, 1 means the user was not logged in. The submit function i have written does not return the error variable after redefinig it on each loop. When i do console.log(error) on each loop it does return 1, but when i check it on click function it returns false. I have the following 2 functions:
I cant seem to understand what i am doing wrong.
function submit(tip,varid){
var error = false;
$.post( "/rwfr.php", { name: ""+tip+"", id: ""+varid+"" })
.done(function( data ) {
var results = jQuery.parseJSON(data);
$(results).each(function(key, value) {
error = value['error'];
return false;
})
});
return error;
}
$(".fa-thumbs-up").click(function(){
var idObj = $(this).parent().parent().attr("data-id");
var act = submit('thumbsup',idObj);
if(act == "1"){
console.log(act);
alert("You must log in before you can rate this video!");
}
});
Though it is down voted, I've kept this answer as someone with similar problem may find this simple and helpful.
The reason it's returning false in your case is that, by the time you get the response from submit(), your next line i.e. if(act=="1") is being run, which will of course return false since you've initialized error with vallue false.
What you can change in your function is that, you may just move your snippet that checks the error in response received inside a callback function which must be called within .done() of your post request.
See below,
// your submit function
function submit(tip, varid, callback){
var response = $.post("post.php", { name: ""+tip+"", id: ""+varid+""});
response.done(function(data){
callback(JSON.parse(data), tip);
});
}
// your callback function
function callbackFunction(response, action_type){
// handle your prompts here based on your action_type i.e. thumbs up, down, favourite, etc.
console.log(response);
if(response.hasOwnProperty("error") && response["error"]=="1"){
console.log("you need to login to do this!");
}
}
// and pass the callback function to your submit function
var act = submit('thumbsup',idObj, callbackFunction);

variable not defined node.js

I'm trying to use Node.js to get a response from an API, I want to clean the API response and use the result.
So to access the first API I have the following code.
To store and use the result I believe I need to store the JSON output globally.
However, I can't work out how to do this.
Example -
var request = require('request');
request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function(err, res, json) {
if (err) {
throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
console.log(car2go);
});
This prints the desired result however I know that this is because my variable is defined within the function.
I want to access the variable outside of the function.
To test if I could do this I changed the code to -
var request = require('request');
request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function(err, res, json) {
if (err) {
throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
});
console.log(car2go);
But if I do this I get
ReferenceError: car2go is not defined
I am running Node v0.12.2 on Mac OS Yosemite (10.10.3).
Admittedly I am very new to node and I am more familiar with R, Python and PL SQL.
There is no way to get reference to it outside of the callback function because the console.log line runs before the callback function is invoked. The reason you have to pass a callback function into the request API is because the request library needs to invoke that function when it's done making the request. Meanwhile, your app moves on and does other things (such as running that console.log line) while it waits for the callback function to fire.
That said, there are a number of ways to deal with asynchronous code. My favorite way is with promises. I use a library called bluebird for handling promises.
var request = require('request');
var Promise = require('bluebird');
var requestP = Promise.promisify(request);
The call to Promise.promisify(request) returns a new function that doesn't take a callback function, but instead returns a promise.
requestP({ url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true })
.spread(function(res, json) {
var car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
})
.then(function (car2go) {
console.log(car2go);
})
.catch(function (err) {
console.error(err);
});
Note: .spread is the same as .then except if the resolved value is an array (which it will be because the callback passed to the request library accepts 2 arguments, which bluebird will translate into an array that the promise resolves to) .spread will split up the array back into multiple arguments passed into the function you give to .spread.
Promise.resolve(['hi', 'there']).then(function (result) {
console.log(result); // "['hi', 'there']"
});
Promise.resolve(['hi', 'there']).spread(function (str1, str2) {
console.log(str1); // 'hi'
console.log(str2); // 'there'
});
You're not going to be able to return that value all the way back out to the same context from which you began the asynchronous call, but you can at least write code that looks somewhat synchronous when using promises.
Without promises you'll be forced to call functions from within functions from within functions from within functions ;)
The response is asynchronous. That means the callback function gets called sometime LATER in the future so your console.log(car2go) is executing BEFORE the callback has even been called.
The only place you can reliably use the response is inside the callback or in a function called from the callback. You cannot use it the way you are trying to. Using asynchronous responses in Javascript requires programming in an asynchronous fashion which means processing results and using results IN the asynchronous callbacks only.
Here's where the console.log() should be:
var request = require('request');
request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function (err, res, json) {
if (err) {
throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
// here is where the result is available
console.log(car2go);
});

Returning a value from callback function in Node.js [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 facing small trouble in returning a value from callback function in Node.js, I will try to explain my situation as easy as possible. Consider I have a snippet, which takes URL and hits that url and gives the output:
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
});
I tried to wrap it inside a function and return a value like this:
function doCall(urlToCall) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}
Because in my Node.js code, I have a lot of if-else statement where value of urlToCall will be decided, like this:
if(//somecondition) {
urlToCall = //Url1;
} else if(//someother condition) {
urlToCall = //Url2;
} else {
urlToCall = //Url3;
}
The thing is all of the statements inside a urllib.request will remain same, except value of urlToCall. So definitely I need to put those common code inside a function. I tried the same but in doCall will always return me undefined. I tried like this:
response = doCall(urlToCall);
console.log(response) //Prints undefined
But if I print value inside doCall() it prints perfectly, but it will always return undefined. As per my research I came to know that we cannot return values from callback functions! (is it true)? If yes, can anyone advice me how to handle this situation, as I want to prevent duplicate code in every if-else blocks.
Its undefined because, console.log(response) runs before doCall(urlToCall); is finished. You have to pass in a callback function aswell, that runs when your request is done.
First, your function. Pass it a callback:
function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return callback(finalData);
});
}
Now:
var urlToCall = "http://myUrlToCall";
doCall(urlToCall, function(response){
// Here you have access to your variable
console.log(response);
})
#Rodrigo, posted a good resource in the comments. Read about callbacks in node and how they work. Remember, it is asynchronous code.
I am facing small trouble in returning a value from callback function in Node.js
This is not a "small trouble", it is actually impossible to "return" a value in the traditional sense from an asynchronous function.
Since you cannot "return the value" you must call the function that will need the value once you have it. #display_name already answered your question, but I just wanted to point out that the return in doCall is not returning the value in the traditional way. You could write doCall as follow:
function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
// call the function that needs the value
callback(finalData);
// we are done
return;
});
}
Line callback(finalData); is what calls the function that needs the value that you got from the async function. But be aware that the return statement is used to indicate that the function ends here, but it does not mean that the value is returned to the caller (the caller already moved on.)
Example code for node.js - async function to sync function:
var deasync = require('deasync');
function syncFunc()
{
var ret = null;
asyncFunc(function(err, result){
ret = {err : err, result : result}
});
while((ret == null))
{
deasync.runLoopOnce();
}
return (ret.err || ret.result);
}
If what you want is to get your code working without modifying too much. You can try this solution which gets rid of callbacks and keeps the same code workflow:
Given that you are using Node.js, you can use co and co-request to achieve the same goal without callback concerns.
Basically, you can do something like this:
function doCall(urlToCall) {
return co(function *(){
var response = yield urllib.request(urlToCall, { wd: 'nodejs' }); // This is co-request.
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}
Then,
var response = yield doCall(urlToCall); // "yield" garuantees the callback finished.
console.log(response) // The response will not be undefined anymore.
By doing this, we wait until the callback function finishes, then get the value from it. Somehow, it solves your problem.

How to capture results from end of FOR loop with Nested/Dependent APIs calls in Node JS

This is my first JavaScript & Node project and I am stuck….
I am trying to call a REST API that returns a set of Post IDs... and based on the set of retrieved IDs I am trying to call another API that returns details for each ID from the first API. The code uses Facebook API provided by Facebook-NodeSDK.
The problem I am having is that the second API fires of in a FOR Loop…. As I understand the for loop executes each request asynchronously…. I can see both the queries executing however I can’t figure out how to capture the end of the second for loop to return the final result to the user…
Following is the code…
exports.getFeeds = function(req, res) {
var posts = [];
FB.setAccessToken(’SOME TOKEN');
var resultLength = 0;
FB.api(
//ARG #1 FQL Statement
'fql', { q: 'SELECT post_id FROM stream WHERE filter_key = "others"' },
//ARG #2 passing argument as a anonymous function with parameter result
function (result)
{
if(!result || result.error) {
console.log(!result ? 'error occurred' : result.error);
return;
} //closing if handling error in this block
var feedObj
console.log(result.data);
console.log(result.data.length);
for (var i = 0; i<resultLengthj ; i++) {
(function(i) {
feedObj = {};
FB.api( result.data[ i].post_id, { fields: ['name', 'description', 'full_picture' ] },
// fbPost is data returned by query
function (fbPost) {
if(!fbPost || fbPost.error) {
console.log(!fbPost ? 'error occurred' : result.error);
return;
}
// else
feedObj=fbPost;
posts.push(feedObj);
});
})(i);
}// end for
}//CLOSE ARG#2 Function
);// close FB.api Function
NOTE I need to call…... res.Send(post)…. and have tried to call it at several places but just can’t get all the posts… I have removed the console statements from the above code…which have shown that the data is being retrieved...
Thanks a lot for your help and attention....
If you just stick res.send almost anywhere in your code, it will be certain to get called before your posts have returned.
What you want to do in a case like this is to declare a counter variable outside your for loop and set it to zero. Then increment it inside the for loop. Then inside your inner callback (in your case, the one that is getting called once for each post), you would decrement the counter and test for when it hits zero. Below I apply the technique to an edited version of your code (I didn't see what feedObj was actually doing, nor did I understand why you were using the immediately-invoked function, so eliminated both - please let me know if i missed something there).
var posts = [];
FB.setAccessToken(’SOME TOKEN');
var resultLength = 0;
FB.api(
//ARG #1 FQL Statement
'fql', { q: 'SELECT post_id FROM stream WHERE filter_key = "others"' },
//ARG #2 passing argument as a anonymous function with parameter result
function (result)
{
if(!result || result.error) {
return;
} //closing if handling error in this block
var counter = 0;
for (var i = 0; i<resultLengthj ; i++) {
counter++;
FB.api( result.data[ i].post_id, { fields: ['name', 'description', 'full_picture' ] },
function (fbPost) { // fbPost is data returned by query
if(!fbPost || fbPost.error) {
return;
}
posts.push(fbPost);
counter--;
if (counter === 0){
// Let's render that page/send that result, etc
}
});
}// end for
}//CLOSE ARG#2 Function
);// close FB.api Function
Hope this helps.
Essentially you are wanting to do an async map operation for each id.
There is a really handy library for doing async operations on collections called async that has a async.map method.
var async = require('async');
FB.api(
'fql', { q: 'SELECT post_id FROM stream WHERE filter_key = "others"' },
//ARG #2 passing argument as a anonymous function with parameter result
function (result) {
async.map(
result,
function (item, callback) {
FB.api(
item.post_id,
{ fields: ['name', 'description', 'full_picture' ] },
callback
);
},
function (err, allPosts) {
if (err) return console.log(err);
// Array of all posts
console.log(allPosts);
}
);
}
);
You definitely don't need to use this, but it simplifies your code a bit. Just run npm install --save async in your project directory and you should be good to go.

Categories

Resources