Accessing restAPI response outside function in nodejs - node.js

I want to be access array outside the function or outside the loop in nodejs. I written following code.
var result = [];
function setid (swfid){
crud.getswift(swfid).then(function (response) {
console.log("response",response);
result = response;
// res.send(response);
}).catch(function (err) {
return ("error:" + err);
});
console.log("result",result);
}
console.log("result",result);
But its returning null. your suggestions please

You wrote a new statement in the function call and therefore you scoped it. This is one of the things wrong there. Apart from that, as the first person commenting to this answer mentioned, you have an async call here. Therefore, you need to return a promise from setid and wait for the response to get the result.

You're mixing your Aysnc logic with Sync. You won't get the response outside the .then function scope because there's no response available at the time you're trying to get the results.
Try using a callback in the promise - You'd need to invoke the function in the promise callback and send the response as function param, then play with the data.
> Promise / API call etc
.then(() => gotDataCallBack(data));
gotDataCallBack(data){
// handle your data and logic here.
// this will make sure you have the data available before you move ahead with
your application/manipulation logic.
}

Related

Retrieving a value in a function in Node.js

I'm struggling with callbacks in Node.js. I simply want playerNumber to be set to the number of players in my collection of Players. The console.log works, but I can't get the variable out of the function and into the playerNumber variable.
And if there's a simpler way get this value for use in the rest of my backend code, I'm all ears. I'm clearly new at Node.js, but the code always seems more involved than I'm expecting.
Thanks in advance!
var playerNumber = function countPlayers(callback){
Player.count(function(err, numOfDocs) {
console.log('I have '+numOfDocs+' documents in my collection');
callback(err, numOfDocs);
});
}
It's probably async, and it's a typical first-timer experience to want to "get back to normal" on the call chain on the way back from async call. This can't be done, but it's not so bad to live with it. Here's how...
Step 1: Promises are better than callbacks. I'll leave the long story
to others.
Step 2: Callbacks can be made into promises
In the OP case...
// The promise constructor takes a function that has two functions as params
// one to call on success, and one to call on error. Instead of a callback
// call the 'resolve' param with the data and the 'reject' param with any error
// mark the function 'async' so callers know it can be 'await'-ed
const playerNumber = async function countPlayers() {
return new Promise((resolve, reject) => {
Player.count(function(err, numOfDocs) {
err ? reject(err) : resolve(numOfDocs);
});
});
}
Step 3: Yes, the callers must deal with this, and the callers of the callers, and so on. It's not so bad.
In the OP case (in the most modern syntax)...
// this has to be async because it 'awaits' the first function
// think of await as stopping serial execution until the async function finishes
// (it's not that at all, but that's an okay starting simplification)
async function printPlayerCount() {
const count = await playerNumber();
console.log(count);
}
// as long as we're calling something async (something that must be awaited)
// we mark the function as async
async function printPlayerCountAndPrintSomethingElse() {
await printPlayerCount();
console.log('do something else');
}
Step 4: Enjoy it, and do some further study. It's actually great that we can do such a complex thing so simply. Here's good reading to start with: MDN on Promises.

Promise function not returning results

I'm using instagram-scraping module to display all posts with a specific hash tag but I get an issue using a function to return finded items.
// in my middleware
function getInstagramPostsByHashTag(hashtag) {
return ig.scrapeTag(hashtag).then(result => {
console.log(result); // all posts are displayed in my console
return result;
});
}
// in my ejs template i send the function getInstagramPostsByHashTag()
<%=getInstagramPostsByHashTag("instagram")%> // nothing is displayed
You can't return the posts from the function because the function has already returned at this point. That's because .then() is asynchronous. It executes the provided callback when the work (fetching the posts) is done, but the function continues to run after the call to .then(), and because you return nothing, you get nothing.
If you want a function to return the result of an asynchronous operation, you have to return a promise from the function itself. To help developers with that, there's the async function that automatically returns a promise. In an async function, you can wait for other promises with the await keyword. Your function would look like this as an async function:
async function getInstagramPostsByHashTag(hashtag) {
return await ig.scrapeTag(hashtag)
}
But this is redundant because it returns exactly the same as a direct call to ig.scrapeTag(hashtag) would do. I don't really know ejs but I think the best thing you could do is something like this (only pseudocode, again, I don't know how rendering with ejs works):
posts = insta.scrapeTag("blablabla")
then
ejs.render("<%=posts%>", {posts})

Using 'body' returned from Request

I am using https://github.com/request/request.
The example that is given is:
const request = require('request');
request('http://www.google.com', function (error, response, body) {
console.error('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.
});
How can I use body elsewhere in my code? I want something like return body but nothing works. I can't use it anywhere!
You cannot directly return an asynchronous result from outside the request() callback. This is not unique to this particular function, but is how all asynchronous callbacks work in Javascript.
When you call request() it starts an asynchronous operation and turns it over to native code. The JS interpreter then goes about it's merry way executing the rest of your Javscript after this function call (not in the callback, but after that). So, you should be able to immediately see that the body result does not exist yet, but the rest of your Javascript is already executing.
Then, some indeterminate time later (depending upon how responsive the server is that you're contacting and how big the result is), when the JS interpreter has nothing else to do, the callback gets called and the body result is available.
So, the ONLY place in your code that you know the body result is good and is available is INSIDE that callback. So, the general way of programming with an asynchronous operation like this in Javascript is that you use the result inside the callback. Any code that needs to use that result gets put inside that callback or you can put the code in a separate function and call that function from inside the callback and pass the body result as an argument to the function.
If you wanted to wrap this request in a function and communicate back that result to the caller (which is not exactly what you show here, but another way to write the code), I'd suggest you first read How do I return the response from an asynchronous call? because it outlines the various ways you can communicate back the body result from your asynchronous operation (another callback or use promises).
To many newbie Javascript developers this seems somewhat heretical. What do you mean I can't just call a function, get the result and return it from the function? Well, that's the extra complication with Javascripts non-blocking, asynchronous I/O model that is entirely event driven. Once you get down the learning curve, you will find a huge number of advantages in the language that flow from this architectural model, but you will have to deal with this extra complication.
The language is evolving to make this type of programming simpler with the use of promises in async functions and then the use of await to "wait" on a promise. If you use the request-promise library instead of the request library (request-promise is derived from request), then you can deal with a promise as the return value and you have more options.
const rp = require('request-promise');
async getMeSomeData() {
try {
let body = await rp('http://www.google.com');
console.log(body);
// can put code here that uses body
return body; // becomes the resolved value of the returned promise
} catch(err) {
console.log(err);
throw err; // makes sure the returned promise is rejected
}
});
getMeSomeData().then(body => {
// use the body here
}).catch(err => {
// error here
});
Note: I showed possibly using the body value or err inside getMeSomeData() or at the caller. Usually, you would do one or the other, but I wanted to show both ways.

Node.js request function return data

I have gone through questions about returning data from a node JS request call. A common mistake is assuming statements are executed line by line or synchronously, which is not the case here. A question of this type: How to get data out of a Node.js http get request.
My question is a bit different. I have written a function getNumber() which returns the number of results for given query. I am wondering how I return a value retrieved by the callback function? So for instance:
function getNumResults() {
Request(url, function(response) {
var responseJSON = JSON.parse(body);
return(responseJSON.results.count);
});
}
function Request(URL, callback) {
request(URL, function(error, response, body) {
console.log('URL: ', URL);
console.log('error: ', error);
console.log('statusCode: ', response && response.statusCode);
console.log('body: ', body);
callback(body);
});
}
What if I want getNumResults() to return responseJSON.results.count? How could I do this?
What if I want getNumResults() to return responseJSON.results.count? How could I do this?
You can't directly return an async value from getNumResults(). You just can't. The function returns long before the value is even available. It's a matter of timing. That's how async responses work. They finish some indeterminate time in the future, but they are non-blocking so the rest of your Javascript continues to run and thus the function returns before the result is even available.
The ONLY way to get the result out is with a callback of some kind. That applies to both your Request() function and to our getNumResults() function. Once a result is asynchronous, nobody in the calling chain can escape that. Async is infectious and you can never go from async back to synchronous. So, if your getNumResults() wants to get the value back to its caller, it will either have to accept a callback itself and call that callback when it gets the value or it will have to return a promise that is resolved with the value.
Here's how you could do this using promises (which are the future of async development in Javascript):
// load a version of the request library that returns promise instead of
// taking plain callbacks
const rp = require('request-promise');
function getNumResults(url) {
// returns a promise
return rp(url).then(body => {
// make the count be the resolved value of the promise
let responseJSON = JSON.parse(body);
return responseJSON.results.count;
});
}
Then, you would use getNumResults() like this"
getNumResults(someURL).then(count => {
// use the count result in here
console.log(`Got count = ${count}`);
}).catch(err => {
console.log('Got error from getNumResults ', err);
});
FYI, I think you can also get the request() library to parse your JSON response for you automatically if you want by setting an appropriate option {json: true}.
EDIT Jan, 2020 - request() module in maintenance mode
FYI, the request module and its derivatives like request-promise are now in maintenance mode and will not be actively developed to add new features. You can read more about the reasoning here. There is a list of alternatives in this table with some discussion of each one. I have been using got() myself and it's built from the beginning to use promises and is simple to use.

Using Mongodb variables out of its functions

So I'm making a web application and I'm trying to send variables to an EJS file but when they are sent out of the mongo functions they come out as undefined because it's a different scope for some reason. It's hard to explain so let me try to show you.
router.get("/", function(req, res){
var bookCount;
var userCount;
Books.count({}, function(err, stats){
if(err){
console.log("Books count failed to load.");
}else{
bookCount = stats;
}
});
User.count({}, function(err, count){
if(err){
console.log("User count failed to load.")
}else{
userCount = count;
console.log(userCount);
}
});
console.log(userCount);
//Get All books from DB
Books.find({}, function(err, allbooks){
if(err){
console.log("Problem getting all books");
}else{
res.render("index", {allbooks: allbooks, bookCount: bookCount, userCount: userCount});
}
});
});
So in the User.Count and Books.count I'm finding the number of documents in a collection which works and the number is stored inside of the variables declared at the very top.
After assigning the numbers like userCount i did console.log(userCount) which outputs the correct number which is 3, If was to do console.log(userCount) out of the User.count function it would return undefined, which is a reference to the declaration at the very top.
What is really weird is that Book.Find() has the correct userCount even though its a totally different function. The whole goal im trying to accomplish is doing res.render("index", {userCount: userCount}); outside of the Books.find(). I can do it but of course for some reason it passes undefined instead of 3. I hope this made a shred of sense.
I seem to have found a solution. but if anyone knows a different way I would love to know. So basically all you need to do is move the User.Count function outside of the router.get() function. Not completely sure about the logic of that but it works...
This is a classic asynchronous-operation problem: Your methods (Books.count, Books.find, User.count) are called immediately, but the callback functions you pass to them are not. userCount is undefined in your log because console.log is called before the assignment in the callback function is made. Your code is similar to:
var userCount;
setTimeout(function() {
userCount = 3;
}, 1000);
console.log(userCount); // undefined
User.count takes time to execute before calling back with the result, just like setTimeout takes the specified time to execute before calling its callback. The problem is JS doesn't pause and wait for the timeout to complete before moving on and calling console.log below it, it calls setTimeout, calls console.log immediately after, then the callback function is called one second later.
To render a complete view, you need to be sure you have all of the data before you call res.render. To do so you need to wait for all of the methods to call back before calling res.render. But wait, I just told you that JS doesn't pause and wait, so how can this be accomplished? Promise is the answer. Multiple promises, actually.
It looks like you are using Mongoose models. Mongoose has been written so that if you don't pass a callback function to your methods, they return a promise.
Books.count({}) // returns a promise
JS promises have a method then which takes a callback function that is called when the promise has been resolved with the value of the asynchronous method call.
Books.count({}) // takes some time
.then(function(bookCount) { // called when Books.count is done
// use the bookCount here
})
The problem is, you want to wait for multiple operations to complete, and multiple promises, before continuing. Luckily JS has a utility just for this purpose:
Promise.all( // wait for all of these operations to finish before calling the callback
Books.count({}),
User.count({}),
Books.find({})
)
.then(function(array) { // all done!
// the results are in an array
bookCount = array[0];
userC0unt = array[1];
allBooks = array[2];
})

Resources