I'm very new to JS and node. I am trying to return a variable from an asynchronous http request, and I recognize that the problem is the async nature of the call, but cannot figure out how to make a very simple code snippet function despite reading multiple tutorials.
Can someone please mark this up such that it works and I can digest it and modify for my own use? Should I be using the async library? I've taken a couple of attempts and haven't been able to make that work.
var request = require('request');
a = 0;
a = foo();
function foo() {
var num = 1;
request('http://www.google.com', function (error, response, body) {
// console.log('error:', error); // Print the error if one occurred
//console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
//console.log('body:', body); // Print the HTML for the Google homepage.
num = 3;
return callback(num);
})
return num;
}
console.log(a);
So I am going to assume that you are making your request correctly, and instead focus on the idea of the callback. If you try this and still need a little more help let me know.
I will try to keep this code close to what you already have:
function foo(callback){
request('http://www.google.com', function (error, response, body) {
if(error)
console.log(error);
//console.log('statusCode:', response && response.statusCode);
// Print the response status code if a response was received
//console.log('body:', body); // Print the HTML for the Google homepage.
num = 3;
callback(num);
})
}
then in order to print a 3 out you would do something like this.
foo(function(value){
console.log(value);
})
You would see the value 3 printed out in this case.
If you wanted to return more then just 3 you would need to pass them as parameters to the callback function that has been passed in. If you were to try to follow callback style more you would probably get something closer to this.
function foo(callback){
request('http://www.google.com', function (error, response, body) {
if(error)
callback(error,null);
//do what ever you need to with the response and body
num = 3;
callback(null,num);
})
}
foo(function(err, value){
if(err)
console.log(err);
console.log(value);
})
You just need to remember that with callbacks, like Rob mentions, you typically don't return. Instead, you have your function call another function that you pass in, and provide that function with parameters that your function determined.
You must/can only operate on the results of an async call within its callback because
callbacks do not return values,
callbacks do not have access to your surrounding context, so in your example num is not defined when the callback is triggered, and
you cannot know when the callback completes and thus its result is available.
In your example code, foo will always immediately return 1 irrespective of the response/body returned from request.
Related
Summary
I have a function where I use crypto.randomBytes to generate a token and I'm having trouble returning the token from the function. I want to return token from createResetToken. My function is below and I've tried many different things but they aren't working. Any help would be greatly appreciated!
Code
function createResetToken() {
crypto.randomBytes(20, function(err, buf) {
const token = buf.toString("hex");
console.log("token inside inside", token);
return token;
});
}
Easiest way of doing this is using sync way of randomBytes(), you can make it by just not providing a callback function:
function createResetToken() {
return crypto.randomBytes(20).toString("hex");
}
By docs:
If a callback function is provided, the bytes are generated
asynchronously and the callback function is invoked with two
arguments: err and buf. If an error occurs, err will be an Error
object; otherwise it is null. The buf argument is a Buffer containing
the generated bytes.
...
If the callback function is not provided, the random bytes are
generated synchronously and returned as a Buffer. An error will be
thrown if there is a problem generating the bytes.
So i've been doing some reading and I think I have a general grasp on this subject but could use some insight from someone more experienced. I've been trying to write a simple RSS reader in Meteor and have been facing some issues with calling the Meteor method asynchronously. I currently define the method on the server(synchronously) and call it on the client(asynchronously). What I don't understand is that when I try to make the HTTP.call on the server, I return an undefined value passed to my client if I pass a callback into the request. But when I make the API request synchronously everything seems to work fine. Is this the normal behavior I should expect/the way I should be making the API call?
Meteor.methods({
getSubReddit(subreddit) {
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const response = HTTP.get(url, {}, (err, res) => {
if(!err) {
//console.log(res.content);
return res;
} else {
return err;
}
});
}
});
Here's the method defined on the server side. Note that logging res.content shows that I'm actually getting the right content back from the call. I've tried reading some other answers on the topic and seen some things about using Future/wrapAsync, but I'm not sure I get it. Any help would be greatly appreciated!
The HTTP.get is doing async work, so callback passed to it will be called out of this meteor method call context.
To get desired result you should do it like this:
Meteor.methods({
getSubReddit(subreddit) {
// IMPORTANT: unblock methods call queue
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const httpGetSync = Meteor.wrapAsync(HTTP.get);
try {
const response = httpGetSync(url, {});
//console.log(response.content);
return response.content;
} catch (err) {
// pass error to client
throw new Meteor.Error(...);
}
}
});
I tried to call res() after some async stuff finishes, inside Async.waterfall([], cb)
But as it seems, the req/res objects are not available in that scope. I call them from my callback cb.
function (req, res, next) {
var query = req.query;
async.waterfall([
async.apply(userManager.register, query.username, query.email, query.password)
], function (err, result) {
if (err)
console.log(err);
if (err && err.internal == false)
return res(err); //TypeError: res is not a function
console.log(result);
});
The only solution I have in mind is, passing the req/res to my backend, and call it there.
But that would mean that my background code needs to have a req and res object. Moreover it returns something to my server, which is also bad.
Thanks for your help.
Your issue is not about scope. It's that res is not a function so you can't call it like res(err). res is an object with methods. You can send an error response either like this which will go to the default error handler in Express:
next(err)
Or like this:
res.status(500).send("Internal Error occurred").
which sends a 500 status on the response and then sends whatever content you want to describe the error.
I can't think of any circumstance where you want to pass the res object into your backend. Your backend should fetch and return data and then your route handler should turn that data or error into a response.
I am working with nodejs and in the JS file i have the code below to retrieve data .
when i try to use the data outside the scope it doesn't work i get the content undefined the whloe time ..
var data = {};
request.get({url: 'https://my-host/Mypath'}, function(err, response, body) {
if (err) {
console.error(err);
data.err = err;
}
data= body;
});
console.log('Data: ', data);
My main problem is that i have to send res.render with the data and i need to do multiple requests to the server .
You are making an async call. console.log() is executed before the request. Try moving that console.log into the request callback and it will work.
To use it out of the request function, you need to return a promise. Maybe try the Q tool:
https://github.com/kriskowal/q
I am trying to make a scraper, but I cant seem to get the code to execute in the right order. I need the album/albumart request function to execute after the title and artist function. I know node.js is weird about this sort of thing, but I've tried moving things all over and still no luck.
Here's the Code
Please pardon the mess and excess debug code.
Current output:
TESTED!!!
req
No error
Pentemple - Pazuzu 2
Now Playing: Pentemple - Pazuzu 2
10
Pentemple
10
Pentemple
1
{ artist: '',
title: '',
album: '',
albumArt: '',
testval: 'TESTED!!!' }
xtest
Because of the asynchronous request calls, the responses might not be in order therefore to keep the order, you will need to make next request call in previous request's callback. Below is the example for the same -
request(url1, function(err, res, html){
if(!err)
{
// url1 successfully returned , call another dependent url
request(url2, function(err2, res2, html2){
if(!err2)
{
// url2 successfully returned, go on with another request call and so on ...
}
});
}
else
{
// first call failed, return gracefully here --
callback(err); // if you have any
}
})
However, as suggested in earlier answer as well, this is anti pattern and will result in messy and cluttered code known as pyramid of doom or callback hell.
I would suggest going with wonderful async npm module and then the same code can be written as -
var async = require('async');
async.waterfall([
function(callback) {
request(url1, function(error, res, html){
callback(null, res, html);
});
},
function(res1, html1, callback) {
request(url1, function(error, res, html){
callback(null, res1, html1, res, html);
});
} // ... AND SO ON
], function (err, result) {
// the result contains the response sent by the last request callback
if(!err)
{
// use your data
}
});
JavaScript is asynchronous. If the requests are dependent on each other I recommend using callbacks so that when one request is complete, it calls the next one.
In most cases performing a request in Javascript has an asynchronous nature. That means that requests do not block the entire process. To perform and action when the request is done callbacks are used. Callbacks are functions that are added into the event loop queue once the request gets in finished state. The easiest way (but for sure not the best one) to make reqeust run one after another is to call second request in the first callback, third request in seconds callback and so on.
request(profileurl, function (error, response, html) {
console.log("req");
if (!error) {
// ...
request(albumurl, function (error, response, html) {
if (!error) {
// ...
request(albumurl, function (error, response, html) {
// ...
});
});
} else {
console.log("ERROR: " + error);
}
});
But such a practice is considered to be anti-pattern and is called Pyramid of Doom, because nesting callbacks make the code unreadable, hard to to test and hard to maintain.
Good practice is considered to use promises. They come "in box" with ES2015. But if you use ES5, you should use some additional module for them like: request-promise or Q.