Can't change the var value outside function - node.js

I need to change the value of result var, but I'm not getting.
My code:
var request = require('request');
var result = ""
function getQuote(callback){
return new Promise((resolve,reject) => {
request({'method': 'GET','url': 'https://blockchain.info/ticker'}, function (error, response) {
if (error) return reject(error);
return resolve(callback(response.body))
})
})
}
getQuote((data) => { result = data })
console.log(result) // return is empty
Thanks!

getQuote is an asynchronous function. When you call console.log(result), it may not call the callback function (data) => { result = data } you passed in while getQuote is still waiting for the response of your request. So, result is still the initialized value which is empty string.
try to log the result after the promise is fulfilled, such as getQuote((data) => { result = data }).then(()=>console.log(result))

Javascript engine will not wait for the promise to finish executing. Immediately after calling getQuote(), it will proceed executing the next line, i.e, console.log(result), which is simply empty and not yet assigned any value, because it will take some time to finish completing Promise-Get method.
You need to research and understand Javascript Execution Context and Event Looping.
https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff

Related

What is Async in Nodejs

If I am trying to write an app.post() function why do some examples use async and some just writes a regular function(req,res)? What does async do that is different from regular function?
async (Asynchronous) function gives your code the ability to pause for any action. Let's see some examples:
SamplePost = (data) => {
let result;
result = Request.Send("POST", data);
console.log(result);
}
If you run the above function with an actual POST request It'll print null because by the time the result of the request will be fetched the console.log will finish executing.
SamplePost = async (data) => {
let result;
result = await Request.Send("POST", data);
console.log(result);
}
But in the above code it will print the actual result. Because this time the code will pause at the async and as long as it doesn't return any value (Not a Promise) it'll keep waiting and as soon as it'll get a return value it'll continue the code.
Sorry in advance for overcomplicating

How the while loop works with promise and callback function in Nodejs?

This is my first time to write while loop with Promise and callback. I don't know why it leads to infinite loop. How to fix it?
async function getResult(){
return new Promise((resolve, reject) => {
let params ="some input and setting";
let next = "hasNext";
let array = [];
let error = null;
while(next !== null){
checkNext(params, function(err,data) { //checkNext is a function to return the current list and check wether there has the next list
if(err){
next = null;
error = err;
}else{
next = data.hasNext; // if there is not next list, data.hasNext = null
array = array.concat(data.array); // data.array return the current list
}
});
}
if(error !== null){
reject(error);
}else{
resolve(array); // I want to return all lists
}
});
}
It leads to an infinite loop because checkNext() is asynchronous and non-blocking so your while() just runs forever before even one call to checkNext() gets a chance to finish and call its callback.
You never use a while() loop waiting for some asynchronous things to finish in Javsacript (except with await as shown below) because with the event driven architecture of nodejs, the while loop never returns control back to the event loop so no asynchronous operation can never get its completion event processed and thus the thing you are waiting for never gets a chance to happen. Things are different (as shown below) if you use await to await a promise that is connected to your asynchronous event. Then, you can use a while() loop successfully.
With asynchronous operations where you're going to use promises, you pretty much always want to promisify your asynchronous operations so all your control flow is with promises, not plain callbacks as the two don't mix very well. This is what I would suggest:
const { promisify } = require('util');
const checkNextP = promisify(checkNext);
async function getResult() {
let params = "some input and setting";
let next = "hasNext";
let array = [];
while (next !== null) {
let data = await checkNextP(params);
next = data.hasNext; // if there is not next list, data.hasNext = null
array = array.concat(data.array); // data.array return the current list
}
return array;
}
Here, the while loop works because we're using await with a promise returned from checkNextP() and that await suspends the execution of the function until that promise resolves/rejects.
A little more explanation about how async functions work
At the point we hit the first await this async function will automatically return a promise. The caller will get that promise at that point. Then, when the promise with that first await resolves, the function will resume, you will get the first data value and the rest of your loop executes. This process will repeat until next is null. At that point, your while() loop will be done and the return array statement will execute. Because this is an async function, what that return statement really does is it resolves the promise that the async function previously returned and sets the array to be the resolved value of that promise.
If, the promise from checkNextP() rejects, then the await checkNextP() will throw the rejection and since we don't have a try/catch around it, the async function will automatically catch that throw and it will reject the promise that the async function has previously returned, causing the caller to get a promise rejection with whatever error checkNextP() rejected with. So, the error handling here works too.
The caller of getResult(), just needs to do something like this:
getResult().then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
Or, the caller could also be in an async function itself and use await and try/catch to catch errors.

Best Way to Chain Callbacks?

I have a question about the best way to chain callbacks together while passing data between them. I have an example below which works, but has the flaw that the functions have to be aware of the chain and know their position / whether to pass on a call back.
function first(data, cb1, cb2, cb3){
console.log("first()", data);
cb1(data,cb2, cb3);
}
function second(data, cb1, cb2) {
console.log("second()",data);
cb1(data, cb2);
}
function third(data, cb) {
console.log("third()",data);
cb(data);
}
function last(data) {
console.log("last() ", data);
}
first("THEDATA", second, third, last); // This work OK
first("THEDATA2", second, last); // This works OK
first("THEDATA3", second); // This doesn't work as second tries to pass on a callback that doesn't exit.
I can think of a number of ways around this, but they all involve in making the called functions aware of what is going on. But my problem is that the real functions I’m dealing with already exist, and I don’t want to modify them if I can avoid it. So I wondered if I was missing a trick in terms of how these could be called?
Thanks for the answers, and I agree that promises are probably the most appropriate solution, as they meet my requirements and provide a number of other advantages.
However I have also figured out how to do what I want without involving any extra modules.
To recap the specific ask was to:
chain together a number of functions via callbacks (this is so the first function can use a non-blocking I/O call the other functions are dependent upon),
while passing arguments (data) between them, and
without the existing functions having to be modified to be aware of their position in the callback chain.
The 'trick' I was missing was to introduce some extra anonymous callback functions to act as links between the existing functions.
// The series of functions now follow the same pattern, which is how they were before
// p1 data object and p2 is a callback, except for last().
function first(data, cb){
console.log("first()", data);
cb(data);
}
function second(data, cb) {
console.log("second()",data);
cb(data);
}
function third(data, cb) {
console.log("third()",data);
cb(data);
}
function last(data) {
console.log("last() ", data);
}
// And the named functions can be called pretty much in any order without
// the called functions knowing or caring.
// first() + last()
first("THEDATA", function (data) { // The anonymous function is called-back, receives the data,
last(data); // and calls the next function.
});
// first() + second() + last()
first("THEDATA2", function (data) {
second(data, function (data){
last(data);
}); // end second();
}); // end first();
// first() + third()! + second()! + last()
first("THEDATA3", function (data) {
third(data, function (data){
second(data, function (data){
last(data);
}); // end third();
}); // end second();
}); // end first();
Promises are the way to go as they provide following benefits :
Sequential callback chaining
Parallel callback chaining (kind of)
Exception handling
Easily passing around the objects
In general, a Promise performs some operation and then changes its own state to either rejected - that it failed, or resolved that its completed and we have the result.
Now, each promise has method then(function(result), function(error)). This then() method is executed when the Promise is either resolved or rejected. If resolved, the first argument of the then() is executed, with the result and if Promise gets rejected 2nd argument is executed.
A typical callback chaining looks like :
example 1 :
someMethodThatReturnsPromise()
.then(function (result) {
// executed after the someMethodThatReturnsPromise() resolves
return somePromise;
}).then(function (result) {
// executed after somePromise resolved
}).then(null, function (e) {
// executed if any of the promise is rejected in the above chain
});
How do you create a Promise at the first place ? You do it like this :
new Promise (function (resolve, reject) {
// do some operation and when it completes
resolve(result /*result you want to pass to "then" method*/)
// if something wrong happens call "reject(error /*error object*/)"
});
For more details : head onto here
Best practice would be to check whether the callback you are calling is provided in the args.
function first(data, cb1, cb2, cb3){
console.log("first()", data); // return something
if(cb1){
cb1(data,cb2, cb3);
}
}
function second(data, cb1, cb2) {
console.log("second()",data); // return something
if(cb1){
cb1(data, cb2);
}
}
function third(data, cb) {
console.log("third()",data); // return something
if(cb){
cb(data);
}
}
function last(data) {
console.log("last() ", data); // return something
}
first("THEDATA", second, third, last); // This work OK
first("THEDATA2", second, last); // This works OK
first("THEDATA3", second);
This will work fine.
Alternatively there are many more options like Promises and Async library e.tc
Maybe you want to try it this way, yes right using "Promise" has more features, but if you want something simpler, we can make it like this, yes this is without error checking, but of course we can use try / catch
function Bersambung(cb){
this.mainfun = cb
this.Lanjut = function(cb){
var thisss = this;
if(cb){
return new Bersambung(function(lanjut){
thisss.mainfun(function(){
cb(lanjut);
})
})
} else {
this.mainfun(function(){});
}
}
}
//You can use it like this :
var data1 = "", data2 = "" , data3 = ""
new Bersambung(function(next){
console.log("sebelum async")
setTimeout(function(){
data1 = "MENYIMPAN DATA : save data"
next();
},1000);
}).Lanjut(function(next){
if(data1 == ""){
return; // break the chain
}
console.log("sebelum async 2")
setTimeout(function(){
console.log("after async")
console.log(data1);
next();
},1000)
}).Lanjut();
Okay this might not be ideal but it works.
function foo(cb = [], args = {}) // input a chain of callback functions and argumets
{
args.sometext += "f";
console.log(args.sometext)
if (cb.length == 0)
return; //stop if no more callback functions in chain
else
cb[0](cb.slice(1), args); //run next callback and remove from chain
}
function boo(cb = [], args = {})
{
newArgs = {}; // if you want sperate arguments
newArgs.sometext = args.sometext.substring(1);
newArgs.sometext += "b";
console.log(newArgs.sometext)
if (cb.length == 0)
return;
else
cb[0](cb.slice(1), newArgs);
}
//execute a chain of callback functions
foo ([foo, foo, boo, boo], {sometext:""})

node.js Trying to set multiple variables from multiple requests

So I've been trying to set a global variable from inside a request, but seem to be getting nothing. The code I'm using
A username for testing is test2 after username=
var forSearching = "test2";
var name = "";
console.log(forSearching);
request("http://mercsystem.esy.es/get.php?username=" + forSearching, function(err, res, body)
{
if (err) return console.error(err);
var main = JSON.parse(body);
if (main.success == "false")
{
message.reply("Sorry, invalid user!")
}
else
{
name = main.Username
}
});
If you insert a console.log(name) right after you set the value, you will see that the value is set just fine.
The issue is likely one of timing. request() is an asynchronous operation. That means calling request() starts the asynchronous operation, then the rest of your code continues to run to completion and then, some time LATER, the callback gets called with the final asynchronous results.
Though you don't show where you are trying to use the name variable, you are probably checking the value of this global variable before the callback has been called and thus before the value has been set.
In node.js, what you are doing is not how you use asynchronous results. It will never work reliably (or at all). Instead, the only place to use your asynchronous result is in the callback itself or in some function you call from the callback and pass the result to.
var forSearching = "test2";
console.log("begin");
request("http://mercsystem.esy.es/get.php?username=" + forSearching, function (err, res, body) {
console.log("in request() callback");
if (err) return console.error(err);
var main = JSON.parse(body);
if (main.success == "false") {
message.reply("Sorry, invalid user!")
} else {
var name = main.Username
console.log(name); // value shows fine here
// use the name variable here or call some function and pass
// the name variable to it
}
});
console.log("after request() call");
// You cannot use the name value here (even if it was in a global) because
// the async callback has not yet been called
If you ran this code with the console.log() statement I've added, you would see this sequence of events:
begin
after request() call
in request() callback
From this sequence, you can see that code after your request() call runs BEFORE the async callback runs. Thus, you cannot use your name variable there, even if it is in a global.

How to get back from inside function syncronized in node.js?

function squre(val) {
main.add(val,function(result){
console.log("squre = " + result); //returns 100 (2nd line of output)
return result;
});
}
console.log(squre(10)); // returns null (1st line of output)
I need 100 as output in both of the lines.
It rather depends on the nature of main.add(). But, by its use of a callback, it's most likely asynchronous. If that's the case, then return simply won't work well as "asynchronous" means that the code doesn't wait for result to be available.
You should give a read through "How to return the response from an AJAX call?." Though it uses Ajax as an example, it includes a very thorough explanation of asynchronous programming and available options of control flow.
You'll need to define squre() to accept a callback of its own and adjust the calling code to supply one:
function squre(val, callback) {
main.add(val, function (result) {
console.log("squre = " + result);
callback(result);
});
});
squre(10, function (result) {
console.log(result);
});
Though, on the chance that main.add() is actually synchronous, you'll want to move the return statement. They can only apply to the function they're directly within, which would be the anonymous function rather than spure().
function squre(val) {
var answer;
main.add(val, function (result) {
console.log("squre = " + result);
answer = result;
});
return answer;
}
You can't, as you have the asynchronous function main.add() that will be executed outside of the current tick of the event loop (see this article for more information). The value of the squre(10) function call is undefined, since this function doesn't return anything synchronously. See this snippet of code:
function squre(val) {
main.add(val,function(result){
console.log("squre = " + result);
return result;
});
return true; // Here is the value really returned by 'squre'
}
console.log(source(10)) // will output 'true'
and The Art of Node for more information on callbacks.
To get back data from an asynchronous function, you'll need to give it a callback:
function squre(val, callback) {
main.add(val, function(res) {
// do something
callback(null, res) // send back data to the original callback, reporting no error
}
source(10, function (res) { // define the callback here
console.log(res);
});

Resources