If I console.log the output from count+1, I get a correct number value. If I output the value of note.note_id, I get undefined. Why is this?
I have tried setting value to a predefined one inside the function.
note.note_id = db.notes.count(function(err, count) {
return count + 1;
});
Hard to answer without knowing what db.notes is but it seems to be something accessing a database. This means it's most likely asynchronous which meansa that the count() method will never return a value but you need to do whatever you want to do with the result inside the callback.
db.notes.count(function(err, count) {
note.note_id = count + 1;
// do more stuff here
});
// do NOT do stuff here. it will run BEFORE the callback has been executed
Related
The following code is supposed to take a string as an input(inMessage here) and split the words in it. It then queries firebase realtime database for an associated key, value and replaces the word with the value retrieved. This modified string has to be sent back as output.
Now, I can't seem to find a way to make the 'msg' a proper string. If I hardcode a string instead of msg I get the output rendered correctly. So how do I make msg a proper string? (I've tried enclosing it with "", toString(), String() and JSON.stringify() - There has to be something I'm missing here)
function queryDB(senderID, inMessage){
var arr=inMessage.split(" ");
console.log(arr);
var i;
console.log('inside queryDB');
var msg="";
for(i=0;i<arr.length;i++){
var x = 'https://oreo-fd681.firebaseio.com/'+arr[i]+'.json';
request({
url: x,
method: 'GET'
}, function(error, response, body) {
console.log(response.body);
if (error) {
console.log('Error making api call ' + error);
} else if (response.body.error){
console.log('Error making api call' + response.body.error);
}
else if(response==null) {
//if not found in DB concatenate whatever arr[i] holds
callback1();
}
else {
//else concatenate the found key
var n=JSON.parse(response.body);
//remove the quotes associated with key value
callback2(JSON.stringify(n.key).replace(/['"]+/g, ''));
}
});
function callback1(){
msg+=(arr[i]);
msg+=" ";
console.log(msg);
}
function callback2(add){
msg+=(add);
msg+=" ";
console.log(msg);
}
}
//add quotes back - not sure of this
sendMessageToUser(senderID, ("\""+msg+"\""));
}
This should not be an issue. Try replacing
msg+= (arr[i]);
with
msg = msg.concat(arr[i]);
use concat in all four msg += assignments
if your arr values are numeric values it could be adding numbers instead.
Your problems are related with the asynchronous nature of the request callback:
You execute sendMessageToUser before any of the requests has returned a result.
Because i is declared with var, the value for i will reach arr.length before any of the requests has returned a result, so in callback1 that value will be useless -- it will always reference arr[arr.length], which is undefined
Assuming that the returned keys are strings, there is no reason to first stringify those as JSON and then remove the quotes. In fact, if a key contains quotes, then those will be lost in that process.
There is no guarantee that the responses to the requests will come back in the same order. So doing msg += may well build a string that has the keys in the wrong order.
Not a bug, but creating functions within a loop should be kept to a minimum. So I would not use callback1 and callback2 like that.
Below is one way to do it, which sticks to the plain old callback pattern. It has these changes:
It collects the keys in an array first, so that you can put a key at exactly the index where it belongs. This way the order in which the responses come in does not matter; the array will at the end still have them in the right order.
It keeps track of how many responses we are still waiting for to come in. That way you can known when you have received everything and then call sendMessageToUser
The variable i is declared with let within the for construct, so that it has block scope, i.e. you get a separate variable for each iteration of the loop. This way, when you reference it in a callback, it will be to exactly that version of the variable.
The callback1 and callback2 functions are replaced with code that can deal with the two variations (when response === null or not).
Here is the code:
function queryDB(senderID, inMessage){
var arr = inMessage.split(" ");
console.log(arr);
console.log('inside queryDB');
// At first, use an array, so you can put back the asynchronous results in the correct order
var msgArray = [];
// Keep track of the number of asynchronous results you are still waiting for
var leftOver = arr.length;
// Use LET to make loop variable block scoped: that way you'll have the same value for
// it when the asynchronous callback is called
for(let i=0; i < arr.length; i++) {
var x = 'https://oreo-fd681.firebaseio.com/'+arr[i]+'.json';
request({
url: x,
method: 'GET'
}, function(error, response, body) {
console.log(response.body);
if (error) {
console.log('Error making api call ' + error);
} else if (response.body.error){
console.log('Error making api call' + response.body.error);
}
else {
// Treat the two cases with the ternary operator
// and put the result at the correct index
msgArray[i] = response === null ? arr[i] : JSON.parse(response.body).key;
console.log(msgArray);
// Detect when you have collected all results
leftOver--;
if (!leftOver) {
// Join all the words together into one string and send it
sendMessageToUser(senderID, msgArray.join(' '));
}
}
});
}
}
Like I said, I stuck to the callback pattern, but things become nicer when you use promises and the Promise.all method. You should look into that.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I don't like to ask a question but I feel I need to know how, or a better way of doing this. I should say first that I'm new to Javascript having done Java before and am new to callbacks as well. I read some other questions, but am still having some difficulty. This is truly horrible.
In rounds.js I have this
exports.getCurrentRound = function(callback) {
exec(header + token + address, function (error, stdout, stderr) {
sys.print('stdout: ' + stdout);
rounds = stdout;
if (error !== null)
callback(null, err);
else
callback(null, currentRound);
});
}
In index.js I have this.
var result;
rounds.getCurrentRound( function (err, cr) {
currentRound = cr;
console.log("the current round is" + currentRound);
});
How can i get result to equal something? Hope that makes sense. Keep in mind i'm running windows. And am reluctant to use promises. Whatever that is. I hope I'm not wasting your time too much.
Thanks.
Alternatively if there's a way to just run this synchronously please tell me what it is. I just want to know the proper way of doing this.
You're nearly there! You've defined result at the top but you now just need to set it.
var result; // default: undefined
rounds.getCurrentRound( function (err, cr) {
currentRound = cr;
console.log("the current round is" + currentRound);
result = currentRound;
});
// result will be set at some point in the future
The problem is that you don't know when result will be set. You can find out by constantly checking if it's set using setTimeout, but even that is asynchronous!
The answer is you need to treat your whole program asynchronously. Whoever needs result will have to pass in a callback.
Promises are just a way to say 'I want the value of this variable whenever it is set'.
var result = new Promise(function(resolve, reject){
rounds.getCurrentRound( function (err, cr) {
currentRound = cr;
console.log("the current round is" + currentRound);
resolve(currentRound); // set the actual value of result
});
});
// you can use the value of result later like this:
result.then(function(resultValue){
console.log('The result is ' + resultValue);
});
But notice that even then, to use the value of result you still need to pass a callback. It's still asynchronous! But the benefit you gain is that you can pass the result promise around your code and other bits of code can use the resolved promise to perform something once the value is set. For instance, let's say you want to use the value of result in two places in your code...
result.then(function(resultValue){
console.log('My code is using result ' + resultValue);
});
// somewhere else
result.then(function(resultValue){
console.log('This other code is also using result ' + resultValue);
});
If you're using the callback method that you first started out with (in your question), both cases where you need to use the value of result need to be inside the callback. But using promises, we've managed to separate them out.
In short, the answer is that you have to think about your code slightly differently, instead of thinking about it as a series of steps, try thinking about it as a series of events that might happen in sequence but might also happen 'whenever they're done' - asynchronously.
I have a table which consist of around 50 rows and columns on a page. I want to fetch all rows from table and write a function to check if 2nd column of all words contains word 'Silver'. I tried below code but it is not working. Can someone please help where i am missing an i am not that great with Javascript promises. If match to string is found, i just want to increment a count and then return count at end.
fetchValues(SearchString){
var matchcount=0;
var count = this.ContentTable.Rows.count();
for (var i=0;i<count-1;i++) {
return this.ContentTable.element(by.css('[id*="DXDataRow'+i+'"]'))
.then((Row) =>{
return this.ContentTable.Row.element(by.css('[id*="uxSetList_tccell'+i+'_1"]'))
.then((Column)=>{
var CoinInfo = this.ContentTable.Row.Column.element(by.css('a')).getText();
if (CoinInfo.indexOf(SearchString)>=0)
{
matchcount =matchcount+1
}
return matchcount;
});
});
}
}
first of all, you're returning a value from inside your for() loop. That guarantees your loop is only ever going to be run once, and you're only ever going to examine one row. Is that what you want? (no, really, I have no idea.) Are you trying to create a lot of promises and combine their results into a single number? You might want to use Promise.all() to combine the values of all promises you create, and return a value from that meta-promise:
var myPromises = [];
for (var i = 0; i < count; i++) {
myPromises.push(this.ContentTable.element(by.css('[id*="DXDataRow'+i+'"]'))
.then(/* ...blah blah blah ... */)
);
}
// return a single value from this function containing data from all promises
return Promise.all(myPromises).then((promiseResultsArray) => {
/* calculate match count */
});
second, I think your promises themselves are written incorrectly. You're assuming that the value of i when your promise's callback is run is the same as the value of i when your promise's callback was defined. Because of how promises work in JS, that's not actually the case.
What's happening is that your for() loop creates a bunch of promises and then, at some undefined point in the future, one of these promises gets resolved and the function you passed to its .then() method gets run. This function knows about i - it's right there in the function body, after all! - but the value of i right now is equal to the value of count - 1. We're in the future now, remember, and the for() loop has terminated, and the termination condition for your loop is when i === count - 1. This will be the case for every single promise you create in this loop, because they all get executed after the loop terminates.
You can fix this in a bunch of ways. Probably the cleanest is to declare a new variable, initialized to the current value of i and never ever changed, and refer to that inside your .then() callback:
var i = 0;
var myPromises = [];
for (i = 0; i < count; i++) {
var currentCount = i;
myPromises.push(this.ContentTable.element(by.css('[id*="DXDataRow'+currentCount+'"]'))
.then(/* ...blah blah blah ... */)
);
}
// return a single value from this function containing data from all promises
return Promise.all(myPromises).then((promiseResultsArray) => {
/* calculate match count */
});
if you want more information, there are plenty of SO questions about how to use promises.
I have a list of checkboxes that I want to check if they are checked. I want to loop through the array of objects and check one by one but I cannot get the size of the array. I have tried the following 2 ways but I get different errors.
var i, n;
networkStatusDetail.detailsList.all(by.tagName('div')).then(function(arr){
console.log(arr.length);
for(i=0;i<; i++){
arr.get(i).element(by.model('displayedsites[site.siteid]')).isSelected().then(function(checked){
expect(checked).toBe(true);
});
}
});
With this code above I get an error saying that arr.get() is not a valid function.
The following code does not work because I am unable to get the size of the array. Assigning the size of the array to another variable only works within a promise function, but once the variable is used outside the value is gone.
networkStatusDetail.detailsList.all(by.tagName('div')).then(function(size){
n = size;
console.log('n = '+n);
});
console.log('n outside '+ n);
for(i=0;i<n; i++){
check.get(i).element(by.model('displayedsites[site.siteid]')).isSelected().then(function(checked){
expect(checked).toBe(true);
// console.log(i);
});
}
the 2 consoles will print 512 and undefined respectively, showing that the value of n outside of the promise function does not work.
I can only make it work if I manually set the max value of the for loop, but this value can change over time so It's not correct.
If anybody could give me a hand on how to do this it would be greatly appreciated.
You don't need to know how many checkboxes are there to loop over them - use map() or each() instead:
element.all(by.model('displayedsites[site.siteid]')).each(function (checkbox) {
return checkbox.isSelected().then(function (isSelected) {
console.log(isSelected);
});
});
I am new to Javascript, I am confused about the code below for hours.
I wanna know why i can't assign the value of result to htmlContent.
Thanks!
var htmlContent = fs.readFileSync(program.file);
restler.get('http://google.com').on('complete', function(result) {
if (result instanceof Error) {
util.puts('Error: ' + result.message);
} else {
htmlContent = result;
}
});
console.log(htmlContent);
The problem here is that the line starting with restler.get begins before the console.log, but it doesn't finish before it necessarily.
You should put your console.log inside the restler.get call.
I would look up the concept of futures and promises in JS, this might help clear up some fundamentals:
http://monochrome.sutic.nu/2012/04/06/javascript-futures.html
When you are using continuations in JS (a requirement in node.js especially), you really need to get your head around them.
Here, your function inside the .on() will be called asynchronously (in simple words it will get executed when "complete" event occurs i.e. some later time) and you are using console.log(htmlContent) is regular function call, so you can not set htmlContent value inside the function this way.
If you want to use result value in any other function then you should pass this value to your next function. I hope this will work.