getting variable value from inside a callback function - javascript

I am trying to pull a variable's value from a callback function.
What am I doing wrong here?
var storyList = [];
var storyTemp ={};
// get list of all stories
db.getAllStoriesSet(function(err, reply) {
if (err) throw err;
else {
storyList = reply;
console.log("storylist inner: " + storyList);
};
});
console.log("storylist outer: " + storyList);
in the above code, I am expecting the inner and outer storylist values to be same.
however the outer storyList value still comes as a null list.
EDIT: to add to the question.
I have initialized a variable storyList outside the async function. i am then taking the reply from the async function and putting it in the variable storyList. I was hoping to cheat my way out of the async pit of slow and brutal death that I have apparently dug for myself.
shouldnt it have worked?

If getAllStoriesSet is asynchronous, then the line following its call is called before the callback is executed. So storyList is just like it was before the call.
That's the reason why you provide a callback : you must put inside the callback the code which needs the value of reply.

Related

Clarification - Why do we need to wrap nested async/awaits in another async/await?

I've been looking more into async/await and trying to understand exactly when to use/not use it. One thing that still puzzles me is whether you make an outer function async when it is only calling a nested async function.
I understand that async/await wraps a return value in a Promise. But does the outer function need to do this wrapping as well, even when it is only responsible for executing the inner async function, and not actually using a return value (thus not needing to wait for one)?
Let's say an example is an app that is posting messages to a server.
const someFunction = (userId) => {
const message = 'Some message'
try {
asyncFunc(userId, message)
} catch (e) {
console.error(e)
}
}
The inner async function is responsible for the actual posting and this will be written with async/await.
If someFunction needs to also return a Promise and therefore be made into an async function, then does this mean any outer function that also calls someFunction will also need to be wrapped? Does every outer function calling an async function have to be wrapped up to the outer most layer?

Variable global defined in a function is showing undefined when called outside the function

I have defined a function by using let and then created a function.
This function send a post request to an api and get data in json format. In this function I have used a variable and set it equal to the value of the response.
when I use this function all works fine.
whenI use this variable outside the function then I get undefined value''
below is my code
var request = require("request");
let conkey = 'Some Value', //varialble for consumer_key
consec = 'Some Value', //varialble for consumer_secret
tkn = 'Some Value', //varialble for token
tknsec = 'Some Value',
value;
function myFunction() {
request.post('someurl', {
oauth: {
consumer_key: conkey,
consumer_secret: consec,
token: tkn,
token_secret: tknsec
},body: {"skillIds": [some value]
},json: true}, function (err, res, body) {
if (err) throw new Error(err);
value = body.sonething.something;
console.log(value); ////THIS WORKS FINE
});
};
myFunction();
console.log(value); ////This prints Undefined
This occurs because request.post is an asynchronous action. Which means it is going to be completed eventually but not immediately. That is also why request.post accepts a function, which is known as the callback function.
function (err, res, body) { ... } will be called only when the request finishes.
Let's walk through your code in order... it will first:
1 - Create your variables.
2 - Define myFunction
3 - Calls myFunction. myFunction will call on request.post. However request.post is asynchronous. This means it will return immediately before it has the result. myFunction now exits.
4 - console.log(value); is now called, but at this point of time, value is undefined.
5 - When the request is completed, function (err, res, body) (defined in the request.post call) will execute
value = body.sonething.something;
console.log(value);
And value here will now be defined.
Hope this helps.
You may try to use js’s fetch api, which is promise based, it will help you solve your issue.
Or some other side libraries, such as axios.
The problem is that your code doesn’t wait for request to be received and console.logs before your response would change Value’s “value”. That’s why I think you get undefined.
Thanks everyone for your answers.
I was able to get over the issue by creating an empty object outside the function and with in function I inserted the values in the empty object.

JS - Assigning a variable while using a callback [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I'm relatively new to JS, and have been stuck on this problem for a while. I am trying to assign a variable from the return value of a function, like this:
var data = makeApiRequest(url);
However, the code continues before the makeApiRequest() function completes, causing data to be undefined.
I've been researching the problem for a while and found that the problem can be fixed with a callback. My issue is I'm not sure how to get data assigned to the result of makeApiRequest() in doing so.
I found this example (on callbackhell.com) that I think perfectly illustrates my problem:
var photo = downloadPhoto('http://coolcats.com/cat.gif')
// photo is 'undefined'!
Author's solution:
downloadPhoto('http://coolcats.com/cat.gif', handlePhoto)
function handlePhoto (error, photo) {
if (error) console.error('Download error!', error)
else console.log('Download finished', photo)
}
console.log('Download started')
The thing that I cannot figure out for the life of me is how photo is assigned (or even declared) in this example. I feel like if I could crack that, I could figure out my specific problem. Any help would be greatly appreciated. Thanks very much in advance.
makeApiRequest(url) is an asynchronous call. That means that if you try to evaluate it immediately after calling it, it will be undefined. The modern solution is to use a Promise.
However, a callback function is perfectly acceptable if your logic won't go layers deep. You can do so by altering the parameters on your makeApiRequest function like so:
function makeApiRequest (url, callback) {
// make the api call, and then:
callback(results);
}
Now, you will call makeApiRequest() as such:
makeApiRequest (url, function(data) {
// handle your data here.
console.log(data);
});
When download function completes, it invokes callback function handlePhoto with data (photo) passed to it as a second argument, so that it can be used inside of the handlePhoto function.
If you want to assign the return value of a promise then you could wrap the variable inside async function and later use await.
** But async and await is a new feature and not widely supported.
So in your case assuming makeApiRequest() returns a promise
async function any(){ /* using async before function declartion*/
var getValue = await makeApiRequest(); // using await
console.log(getValue); // returned value of makeApiRequest
}

Node.js calling a callback function inside a callback

I started learning node.js by reading Node JS in Action book recently. This is probably a newbie question, but after reading several posts of callback functions and javascript scope of variables, I still have problem understand the idea behind this code in chapter 5 of the book.
function loadOrInitializeTaskArray(file, cb) {
fs.exists(file, function(exists) {
var tasks = [];
if (exists) {
fs.readFile(file, 'utf8', function(err, data) {
if (err) throw err;
var data = data.toString();
var tasks = JSON.parse(data || '[]');
cb(tasks);
});
} else {
cb([]);
}
});
}
function listTasks(file) {
loadOrInitializeTaskArray(file, function(tasks) {
for(var i in tasks) {
console.log(tasks[i]);
}
});
}
It includes three callback functions divided in two functions. listTasks(..) is called first and it calls loadorInitializeTaskArray(.. ) later. My problem starts here, how is this call handle by node? loadOrInitializeTaskArray takes two arguments and the second one is the callback function which shouldn't accept any parameters according to is signature but it does!!
when does cb(..) get called in loadorInitializeTaskArray and what is that (the same function that calls helper function)?
"tasks" is an array declared inside function loadOrInitializeTaskArray, how do we have access to it in listTasks(..) function?
I know in Javascript, scope of a variable is inside the function it define and all nested functions. But I have a hard time understanding it here. So can someone explain what is going on here?
Thank you
You really are having a hard time in understanding the scope of variable inside the nested functions. So, lets start with it.
Let's consider this code
function foo(){
var a = 3;
function bar(){
console.log(a);
}
return bar;
}
var baz = foo();
baz(); // the value of a i.e 3 will appear on console
If you know langauges like C, C++... Your brain would be interpreting this code like this. If you don't know any of them just ignore thiese points.
When foo() is called, variable a will be declared and assigned value 3.
When foo is returned the stack containing a will be destroyed. And hence a would be destroyed too.
Then how in the world baz() is outputting 3. ???
Well, in javascript when a function is called the things that happen are different than in C. So, first let your all C things go out from your mind before reading further.
In javascript scope resolution is done by travelling down a chain of objects that defines variables that are "in scope" for that code. Let's see how?
When you declare a global JavaScript variable, what you are actually doing is defining a property of the global object.
In top-level JavaScript code (i.e., code not contained within any function definitions), the scope chain consists of a single object, the global object.
In a non-nested function, the scope chain consists of two objects. The first is the object that defines the function’s parameters and local variables, and the second is the global object.
In a nested function, the scope chain has three or more objects. When a function is defined, it stores the scope chain then in effect. When that function is invoked, it creates a new object to store its local variables, and adds that new object to the stored scope chain to create a new, longer, chain that represents the scope for that function invocation.
So, when foo() is executed. It creates a new object to store it's local variables. So, variable a will be stored in that object. And also, bar is defined. When bar is defined it stores the scope chain in effect. So, the scope chain of bar now contains the object that has variable a in it. So, when bar is returned it has a reference to it's scope chain. And hence it knows a.
So, I guess it answers your question how node handles the code.
You wrote:
loadOrInitializeTaskArray takes two arguments and the second one is the callback function which shouldn't accept any parameters according to is signature but it does!!
The callback function is
function(tasks) {
for(var i in tasks) {
console.log(tasks[i]);
}
}
And it accepts an argument tasks. So, you are wrong here.
And When loadOrIntializeTaskArray is called cb refers to this call back function. And cb(arg) basically does this tasks = arg where tasks is the argument in callback function.
I guess you still will be having a lot of questions. Let me know in comments. And I highly recommend you to go through the Core Javascript before diving into node.
First, there is not really any such thing as a function signature in javascript. You can pass as many or as few arguments to a function as you like. The second argument to loadOrInitialiseTaskArray is simply assigned to the local variable cb when the function is called. The line cb(tasks) then invokes this value, so the second argument had better have been a function.
When you call loadOrInitializeTaskArray from listTasks, the second argument is indeed a function, and the first argument to this function is named tasks in its own scope. It isn't reaching into loadOrInitializeTaskArray and using the variable declared in that function's scope. You explicitly passed in that value when you invoked cb.
The code would work the same and perhaps be easier to understand if we renamed the variables:
function loadOrInitializeTaskArray(file, cb) {
fs.exists(file, function(exists) {
var taskArray = [];
if (exists) {
fs.readFile(file, 'utf8', function(err, data) {
if (err) throw err;
var data = data.toString();
var taskArray = JSON.parse(data || '[]');
cb(taskArray);
});
} else {
cb([]);
}
});
}
function listTasks(file) {
loadOrInitializeTaskArray(file, function(listOfTasks) {
for(var i in listOfTasks) {
console.log(listOfTasks[i]);
}
});
}
I'm not sure what you mean by "the second one is the callback function which shouldn't accept any parameters according to is signature". The callback signature (function(tasks)) certainly does expect an argument (tasks).
cb is the callback function that was passed in. In this case it is literally the anonymous function:
function(tasks) {
for(var i in tasks) {
console.log(tasks[i]);
}
}
tasks is the name of two different variables (but they point to the same object because arrays are passed by reference) in different scopes. One is defined in loadOrInitializeTaskArray() as you noted, the other is a parameter for the callback passed to loadOrInitializeTaskArray().
Also, on an unrelated note, typically callbacks follow the "error-first" format. This allows for handling of errors upstream. Modifying the code to follow this convention would result in:
function loadOrInitializeTaskArray(file, cb) {
fs.exists(file, function(exists) {
var tasks = [];
if (exists) {
fs.readFile(file, 'utf8', function(err, data) {
if (err)
return cb(err);
var data = data.toString();
// JSON.parse() throws errors/exceptions on parse errors
try {
var tasks = JSON.parse(data || '[]');
} catch (ex) {
return cb(ex);
}
cb(null, tasks);
});
} else {
cb(null, []);
}
});
}
function listTasks(file) {
loadOrInitializeTaskArray(file, function(err, tasks) {
if (err) throw err;
for (var i in tasks) {
console.log(tasks[i]);
}
});
}

Get result.insertId from callback function in node.js

Check my updated answer below the other answers. The question turned out to be quite simple, I just didn't understand how async worked or should be handled at the time of posting it
I have the following code:
function qcallb(err,result){
console.log("result.insertID:"+result.insertId);
return result.insertId;
}
var createRecord= function (tableName,record){
try{
queryStatment="INSERT INTO " + tableName + " SET ?";
var result='';
var query = connection.query(queryStatment, record, qcallb,false);
}
catch(err){
return 0;
}
return iid;
}
I want to return the variable result.insertId from the callback function, to the function that calls create record, but I have not found a way to do this.
I tried to set a global variable in the callback function, but when I try to access it, its value is still unchanged.
Is there a way for me to either access the value directly in the callback function, or to have callback function notify me when it's called then return the value?
EDIT:
After testing every proposed solution here, and those that I found by searching elsewhere, the only thing that worked for me (without using external modules to make a piece of code run in 'sync' rather than 'async'), was to implement the logic I wanted inside the callback itself, which is what I wanted to avoid in the first place, but I couldn't work around it no matter what I did.
2 years later:
After getting much more experience with node.js and async code, I see how naive this question was, although it was beyond me at the time, which is also obvious in my using a try/catch block around an async call which is useless with async code, where handling errors is done by checking for err in the callback and acting upon it if it exists.
Since it seems to have quite a number of views, this seems to be a recurring issue for those newly introduced to async development.
This can't be done in the sense that was asked in the question, for an operation to occur after an async operation and use a value that is a result of the async operation, it has to be called from the callback of the async operation with that value.
To resolve the issue, the code can be modified to:
function doSomethingWithResult(result) {
// query is done, and this would only be called if no error occurred
console.log('Result is: ', result)
// do whatever you want
}
function queryCallback(err,result) {
if (err) {
throw err
}
doSomethingWithResult(result)
}
function createRecord(tableName, record) {
queryStatment='INSERT INTO `' + tableName + '` SET ?'
connection.query(queryStatment, record, queryCallback)
}
---
Old Answer: After testing every proposed solution here, and those that I found by searching elsewhere, the only thing that worked for me (without using external modules to make a piece of code run in 'sync' rather than 'async'), was to implement the logic I wanted inside the callback itself, which is what I wanted to avoid in the first place, but I couldn't work around it no matter what I did.
Yes, 'notify me' is the way to go:
var createRecord= function (tableName,record, cb){
var query = "INSERT INTO " + connection.escape(tableName) + " SET ?";
connection.query(query, record, function(err, res) {
if (err) return cb(err);
cb(null, res.insertId); // this is "notify me" part
});
}
// now use it
createRecord('blah', { foo: "bar" }, function(err, id) {
console.log(id);
});
Also, try/catch won't help much here as callback function is not executed down stack frame but called outside from event loop. You need to use domains/zones or some similar sort of async error handling
i think there is a way with waiting for the response with an endless loop, maybe you can also use the context to assign the variable like so :
var sleep = require('sleep');
var createRecord= function (tableName,record){
try{
queryStatment="INSERT INTO " + tableName + " SET ?";
var insertId=0;
var query = connection.query(queryStatment, record, function(err,result){
console.log("result.insertID:"+result.insertId);
insertId = result.insertId;
},false);
}
catch(err){
return 0;
}
while(insertId==0){
sleep.sleep(0.1); # sleep for 0.1 second
}
return insertId;
}
i haven't tested this code on my machine, please test it and let me know if there is any error.

Categories