This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 6 years ago.
I'm a NodeJS beginner and actually I try to work with a NoSQL Database for building a simple login via. Digest Authentication. I also created an module called "users" wich is loaded in my application.
Now when I try to call the data results inside the callback function I get my correct data. In the same callback I assign the returned data to the variable records. When I call the variable outside I will get the result null - where is the error?
var dbdriver = require('nosql'),
dbfile = dbdriver.load("./db.nosql"),
records = null
dbfile.top(1000).callback(function(err, response) {
(function(users) {
records = users
console.log(users) // Here i get the wanted result
})(response)
})
console.log(records) // Here the result is empty
thank you very much :)
Since the you are calling console.log(users) inside the callback, the value is reflected correctly. And this is a behavior of asynchronous calls in javascript. So the last line console.log(records) would probably be run first before the callback executes and so records up to this point does not have any value.
So I would suggest that you make the a function that does something on users and call it inside the callback.
var dbdriver = require('nosql'),
dbfile = dbdriver.load("./db.nosql"),
records = null
dbfile.top(1000).callback(function(err, response) {
(function(users) {
//records = users
//console.log(users) // Here i get the wanted result
doSomethingToUsers(users);
})(response)
})
function doSomethingToUsers(users) {
// do something to users
}
//console.log(records) // Here the result is empty
Looks like a timing issue to me. When console.log(records) in the outer scope gets called before the (async) callback function was called, records contains exactly the value that was initially assigned (records = null).
Only after calling the async callback, the records variable will be set.
Does this help?
Related
This question already has answers here:
MongoDB atomic "findOrCreate": findOne, insert if nonexistent, but do not update
(4 answers)
Mongoose - Create document if not exists, otherwise, update- return document in either case
(5 answers)
Closed 4 years ago.
I am using Mongoose and am in a situation in which I am looking up a customer from the database. If the customer exists, I will return a customerId. However, if the customer does not exist, I want to create them and then return the customer id. In either case, I want the promise to end with a customerID. Anyway, while I have seen in the documentation that is possible to instantly wrap a return value using "Promise.resolve", I am receiving an error. This feature is apparently useful when trying to make synchronous tasks more congruent with Node by wrapping their return values. Anyway, what is a better way to deal with this or return a promise? At the end of the day I want the code to return the customer object or fall through to a catch/error.
TypeError: Promise.resolve is not a constructor
Below is the code (I edited out some security things, but I think I didn't make any serious alterations).
Customer.findOne({email: email}).then((customer) => {
if (customer) {
return Promise.resolve(customer);
}
else {
return new Customer({email: email}).save();
}
}).then( function(customer) {...
This is really about Promises, not Mongoose or MongoDb. If you are in a situation in which you are inside of a promise and want to return a value to the chain, what do you do?
You don't need Promise.resolve() here. If a then handler returns a non-promise, the promise returned by the then call will resolve with that value. In other words, it will already be wrapped, so wrapping it again is redundant.
On an unrelated note, the else is also redundant, as return statements exit the currently executing function:
Customer.findOne({email: email}).then((customer) => {
if (customer) {
return customer;
}
return new Customer({email: email}).save();
}).then( function(customer) {...
Promise.resolve() and Promise.reject() are generally intended for operations that have special cases where no actual asynchronous operation takes place, but you'd still like them to return promises in those cases for consistency's sake.
As a trivial example, imagine I have a bunch of objects with uid properties that may or may not have a corresponding username property. I want to make sure all of them have a username property, so I write a function I can call on all of them, which will look up the username from a database if it is missing. For consistency I want this function to always return a promise, even if the username is already there and no lookup is actually necessary. Also, I can add a case where, in the event that the uid is missing, the promise will reject without wasting time trying to look up undefined as a uid.
Could look something like this:
function ensureUsername(user) {
if ('username' in user) return Promise.resolve(user);
return lookUpUsernameByUid(user.uid)
.then((username) => {
user.username = username;
return user;
});
}
Promise.resolve() is also quite useful when dealing with values that may or may not be promises. This is because it simply returns the value if it already is a promise.
Let's say you want to write a function foo that invokes some callback function. The callback function may or may not be asynchronous-- i.e. it may return a promise, but is not required to. How do you accomplish this?
It's simple. Just put the result of the callback in Promise.resolve and chain a then onto it:
function foo(cb) {
return Promise.resolve(cb())
.then((callbackResult) => {
// Do whatever...
});
}
This will work whether cb returns a promise that resolves with callbackResult or simply returns callbackResult directly.
I am new to nodejs. Using bluebird promises to get the response of an array of HTTP API calls, and storing derived results in an ElasticSearch.
Everything is working fine, except I am unable to access the variables within the 'then' function. Below is my code:
Promise.map(bucket_paths, function(path) {
this.path = path;
return getJson.getStoreJson(things,path.path);
}, {concurrency:1}).then(function(bucketStats){
bucketStats.map(function(bucketStat) {
var bucket_stats_json = {};
bucket_stats_json.timestamp = new Date();
bucket_stats_json.name = path.name ==> NOT WORKING
});
});
How can I access the path.name variable within the 'then' ? Error says 'path' is undefined.
The best way to do this is to package the data you need from one part of the promise chain into the resolved value that is sent onto the next part of the chain. In your case with Promise.map(), you're sending an array of data onto the .then() handler so the cleanest way to pass each path down to the next stage is to make it part of each array entry that Promise.map() is resolving. It appears you can just add it to the bucketStat data structure with an extra .then() as show below. When you get the data that corresponds to a path, you then add the path into that data structure so later on when you're walking through all the results, you have the .path property for each object.
You don't show any actual result here so I don't know what you're ultimately trying to end up with, but hopefully you can get the general idea from this.
Also, I switched to Promise.mapSeries() since that's a shortcut when you want concurrency set to 1.
Promise.mapSeries(bucket_paths, function(path) {
return getJson.getStoreJson(things,path.path).then(bucketStat => {
// add the path into this item's data so we can get to it later
bucketStat.path = path;
return bucketStat;
});
}).then(function(bucketStats){
return bucketStats.map(function(bucketStat) {
var bucket_stats_json = {};
bucket_stats_json.timestamp = new Date();
bucket_stats_json.name = bucketStat.path.name;
return bucket_status_json;
});
});
I'm unable to get the values in meals object although i have create new object at the top can any one tell which is the best procedure access variable inside callback function
var meals = new Object();
passObj.data = _.map(passObj.data, (x)=> {
x.mealImageUrl = !_.isNull(x.image_url) ? `${config.image_path}${x.image_url}` : x.image;
dbHelpder.query(`select * from meals where meal_category = ${x.category_id}`,(error,result)=>{
meals = x.result;
passObj.total = 555
});
return x;
});
You need to use callback again inside the callback function. :)
You are doing something asynchronous, it means, there are no sequence codes. (At least, I keep this in my mind, don't know how others think about this.) So, the code should be:
function somehow(callback) { // you get the result from callback
var meals = new Object();
passObj.data = _.map(passObj.data, (x)=> {
dbHelpder.query(`select * from meals where meal_category = ${x.category_id}`,(error,result)=>{
meals = x.result;
passObj.total = 555;
callback(meals); // Here you get the result
});
}
return x;
}
So, when you are going to use this function, it should be
function afterMeals(resultMeals) {
// do something on the meals
}
somehow(afterMeals);
Use some other technology can make it a bit clear (like promise), but you can not avoid callback actually.
First of all, I cannot see what passObj exactly is, apparently it is defined elsewhere.
Secondly, callback functions don't function the way you seem to think they do. Typically one reason to use them is to implement asynchronous calls, so returning a value is not of use.
The idea is as follows. Usually you have a call like this:
var myFunc1 = function(){
return 42;
}
var x = myFunc1();
myFunc2(x);
However when myFunc1 is an asynchronous call returning a value is impossible without using some sort of promise, which is a topic on its own. So if myFunc1 was an asynchronous call and the 42 was returned e.g. by a server, then just returning a value caused the value to be null, because the return value is not calculated and received yet, when you arrive at return.
This is a reason for callbacks to be introduced. They work in a way, that allows for asynchronous calls and proceeding the way you want to after the call has finished. To show on the example above:
var myFunc1 = function( myFunc2, params ){
// do async stuff here, then call the callback function from myFunc1
...
myFunc2(x);
}
So the asynchronous function doesn't return anything. It makes the calls or calculations it needs to make and when those are done (in the example that is when x has been declared and assigned a value) myFunc2, which is the callback function in our example, is called directly from the asynchronous function.
Long story short - do what you need to do with x directly inside the callback function.
I've got a problem with redis and nodejs. I have to loop through a list of phone numbers, and check if this number is present in my redis database. Here is my code :
function getContactList(contacts, callback) {
var contactList = {};
for(var i = 0; i < contacts.length; i++) {
var phoneNumber = contacts[i];
if(utils.isValidNumber(phoneNumber)) {
db.client().get(phoneNumber).then(function(reply) {
console.log("before");
contactList[phoneNumber] = reply;
});
}
}
console.log("after");
callback(contactList);
};
The "after" console log appears before the "before" console log, and the callback always return an empty contactList. This is because requests to redis are asynchronous if I understood well. But the thing is I don't know how to make it works.
How can I do ?
You have two main issues.
Your phoneNumber variable will not be what you want it to be. That can be fixed by changing to a .forEach() or .map() iteration of your array because that will create a local function scope for the current variable.
You have create a way to know when all the async operations are done. There are lots of duplicate questions/answers that show how to do that. You probably want to use Promise.all().
I'd suggest this solution that leverages the promises you already have:
function getContactList(contacts) {
var contactList = {};
return Promise.all(contacts.filter(utils.isValidNumber).map(function(phoneNumber) {
return db.client().get(phoneNumber).then(function(reply) {
// build custom object
constactList[phoneNumber] = reply;
});
})).then(function() {
// make contactList be the resolve value
return contactList;
});
}
getContactList.then(function(contactList) {
// use the contactList here
}, funtion(err) {
// process errors here
});
Here's how this works:
Call contacts.filter(utils.isValidNumber) to filter the array to only valid numbers.
Call .map() to iterate through that filtered array
return db.client().get(phoneNumber) from the .map() callback to create an array of promises.
After getting the data for the phone number, add that data to your custom contactList object (this is essentially a side effect of the .map() loop.
Use Promise.all() on the returned array of promises to know when they are all done.
Make the contactList object we built up be the resolve value of the returned promise.
Then, to call it just use the returned promise with .then() to get the final result. No need to add a callback argument when you already have a promise that you can just return.
The simplest solution may be to use MGET with a list of phone numbers and put the callback in the 'then' section.
You could also put the promises in an array and use Promise.all().
At some point you might want your function to return a promise rather than with callback, just to stay consistent.
Consider refactoring your NodeJS code to use Promises.
Bluebird is an excellent choice: http://bluebirdjs.com/docs/working-with-callbacks.html
you put async code into a for loop (sync operations). So, each iteration of the for loop is not waiting for the db.client(...) function to end.
Take a look at this stackoverflow answer, it explains how to make async loops :
Here
I tend to write a function like this:
library.getCookie(request.headers.cookie,function(cookies){
db.query("SELECT name,lastupdate FROM session WHERE id=?",[cookies.SESSID],function(result){
var action = require('./action');
new action({
data : result,
callback : function(){ // this callback's gonna run at the end of the request, after severals other db queries.
require('fs').rename('/session/'+cookies.SESSID,'/session/'+require('crypto').randomBytes(20));
}
}).build();
});
});
I got a feeling that my cookies.SESSID is vunerable to race condition because it will be shared between severals IO events. If my concern is feasible, how can I fix my code?
Declare the variables you want to pass to the more inner callback with var and you will stay safe. Otherwise you are just declaring them as implict global variables and each call of the function will overwrite the value you are expecting to get in the callback;
(the callback inside action could take the value of cookies not like the one you passed when you called the first time but a new value if the function is invoked before the first callback is solved)
I have just answered a similar question, with examples: https://stackoverflow.com/questions/25229739/race-condition-and-common-mistakes