Callbacks, exports and javascript [duplicate] - javascript

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.

Related

How to concatenate array objects and strings to get a string in Javascript?

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.

can't change the a variable value in the javascript function

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.

Return value coming through as undefined

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

losing global variable value

This is for a gameing application
I declare the variable skipnpc which is designed as an indicator that a non player character has used his turn and any AI code related to his behavior is skipped for a period of time. the problem I have is I am loosing the value of skipnpc somehow I indicated where in the console.log commands I issue is related to varaible scope but I don't understand how to fix it.
function npcMovement() {
skipnpc = false;...
sql4 = "SELECT id FROM game_moblist WHERE spawn_id =" + spawnid + " AND posx=" + parseInt(mobpathx[mobpathx.length - 1]) + " AND posy=" + parseInt(mobpathy[mobpathy.length - 1])
connection.query(sql4, function (err, send, fields) {
console.log("skipnpc pathing")
io.sockets.emit('groupmoveresult', send, parseInt(mobpathx[mobpathx.length - 1]), parseInt(mobpathy[mobpathy.length - 1]))
skipnpc = true
console.log("skipnpc=true:" + skipnpc)
});
console.log("skipnpc = false:" + skipnpc)
Later I use
if (skipnpc==false){
...
before any further AI code is attempted
connection.query is executed asynchronous. Thus you get to your final line here before it is done.
To put it real simply, skipnpc is guaranteed to still be false by the time you hit your last console.log(...). You're not giving your connection.query(...) any time to execute before trying to look at its result. Anything that relies on the result of connection.query(...) has to be executed as part of the callback you passed to it; otherwise none of the results will have come in when you try to access them.
Asynchronous programming takes some getting used to. You might be able to reorganize your code using the async module and using its waterfall(...) method so everything doesn't wind up deeply nested. But you've got to realize that if you make an asynchronous call, your code will flow right past it.

JavaScript sleep [duplicate]

This question already has answers here:
What is the JavaScript version of sleep()?
(91 answers)
Closed 9 years ago.
Yes, I know - that question has thousands of answers. please, don't tell me about setTimeout method because - yes, everything is possible with that but not so easy as using sleep() method.
For example:
function fibonacci(n) {
console.log("Computing Fibonacci for " + n + "...");
var result = 0;
//wait 1 second before computing for lower n
sleep(1000);
result = (n <= 1) ? 1 : (fibonacci(n - 1) + fibonacci(n - 2));
//wait 1 second before announcing the result
sleep(1000);
console.log("F(" + n + ") = " + result);
return result;
}
if you know how to get the same result using setTimeout - tell me ;) fibanacci is pretty easy task, because there aren't more than 2 recursions, but how about n-recursions (like fib(1) + fib(2) + .. + fib(n)) and sleep after every "+"? Nah, sleep would be muuuuuch easier.
But still I can't get working example of implementing it. while (curr - start < time) { curr = (...) } is tricky, but it won't work (just stops my browser and then throw all console logs at once).
The question is asking how to implement sleep() in JavaScript, right?
function sleep(ms) {
var start = new Date().getTime(), expire = start + ms;
while (new Date().getTime() < expire) { }
return;
}
I just tested it like so:
console.log('hello');
sleep(5000);
console.log('world');
Works for me.
(As a meta comment: I landed here because I have a particular need for this function. Such needs do come up when you need to block while waiting for a value. Even in JavaScript.)
I dont fully understand what you're asking, but I'm going to answer this part:
if you know how to get the same result
using setTimeout - tell me
The fundamental difference is that sleep (as used in many other languages) is synchronous, while setTimeout (and many other JavaScript-concepts, like AJAX for example) are asynchronous. So, in order to rewrite your function we have to take this into account. Mainly, we have to use a callback to fetch the "return value", rather than an actual return-statement, so it will be used like this:
fibonacci(7, function(result) {
// use the result here..
});
So, as for the implementation:
function fibonacci(n, callback) {
console.log("Computing Fibonacci for " + n + "...");
var result = 0;
var announceAndReturn = function() {
setTimeout(function() {
// wait 1 second before announcing the result
console.log("F(" + n + ") = " + result);
callback(result); // "returns" the value
}, 1000);
};
// wait 1 second before computing lower n
setTimeout(function() {
if (n <= 1) {
result = 1;
announceAndReturn();
}
else {
var resultsLeft = 2;
var handler = function(returned) {
result += returned;
resultsLeft--;
if (resultLeft == 0)
announceAndReturn();
}
fibonacci(n-1, handler);
fibonacci(n-2, handler);
}
}, 1000);
}
I would also like to point out that, no, this is not an easier solution than using sleep. Why? Because this code is asynchronous and that's simply more complicated than synchronous code for what most people are used to. It takes practice to start thinking in that way.
The upside? It allows you to write non-blocking algorithms that outperforms their synchronous counterparts. If you haven't heard of Node.js before, you could check it out to further understand the benefits of this. (Many other languages have libraries for dealing with async IO as well, but as long as were talking about JavaScript..)
The trouble with a sleep() type function within a browser (or any other GUI environment for that matter) is that it is an event-driven environment, and wouldn't be able to sleep() in the way you're describing it.
The setTimeout() method works because it is creating an event, and setting the trigger for that event to be a point in time. Therefore the system can give over control of the waiting to the event handler and Javascript itself is free to carry on doing other things.
In the web browser, virtually everything works this way. Mouse click/hover/etc functions are event triggers. Ajax requests don't sit and wait for the response from the server; they set an event to trigger when the response is received.
Time based actions are also done with event triggers, using functions like setTimeout().
This is how it's done. In fact this is how it's done in pretty much any well-written GUI application, because all GUI interfaces must be able to respond to events such as mouse clicks virtually instantly.
A Javascript sleep() function (especially the way it's been implemented in another answer here!) would basically have the effect burn up your CPU cycles while it waited for the clock. The sleep() would remain the active process, meaning that other events may not be processed straight away - which means your browser would appear to stop responding to mouse clicks, etc for the duration of the sleep. Not a good thing.
setTimeout() is the way to go. There is always a way to do it; the resulting code may not be neat and linear like your example code, but event-driven code very rarely is linear - it can't be. The solution is to break the process down into small functions. you can even embed the subsequent functions inside the setTimeout() call, which may go some way to helping you keep your code at least having some appearance of being linear.
Hope that helps explain things a bit for you.
Just use a better algorithm without loops or recursion, and avoid the need for setTimeout() / sleep().
function fibonacci(n) {
return Math.round(Math.pow((Math.sqrt(5) + 1) / 2, Math.abs(n)) / Math.sqrt(5)) * (n < 0 && n % 2 ? -1 : 1);
}
Usage example:
// Log the first 10 Fibonacci numbers (F0 to F9) to the console
for (var i = 0; i < 10; i++) {
console.log(fibonacci(i));
}
Why do you want to 'sleep' while computing anything? Sleeping for any time is nearly always a bad idea in any language. It essentially tells the thread to stop doing anything for that period of time.
So, in a language like javascript that only has one thread (forgetting 'web workers'), what benefit would you reap for pausing ALL computation? It's a bad idea, forget it.
Now to the problem that you've written, though I don't think this is your actual problem. Why do you want to pause for a second while computing this sequence? Even to compute the first 6 numbers in the sequence, it's going to take 8 or so seconds. Why? What possible reason is there to pause for a second between recursive calls? Stop it. Remove it.
If you want to just yield the final result a second after it completes, then use setTimeout with a function uses the answer in some way.
function fib(n) {
...
result = fib();
...
setTimeout(function() {
console.log("Fib for " + n + " is " + result);
},1000);
}
Do not try to implement 'sleep'. Do not pause during computation.

Categories

Resources