Consider the following code:
function dbTask(q) {
mysql = new MySQL();
mysql.host = sqlHost;
mysql.user = sqlUser;
mysql.password = sqlPassword;
mysql.query("USE stock");
return mysql.query(q, function(err, results, fields) {
if(err) {
console.log("MySQL Error: " + err + ", Query: " + q);
return false;
} else {
return results; //here
}
});
};
var r = dbTask("SELECT * FROM user;");
console.log(r);
Whereas, I want the results to be returned from the inner anonymous function when calling dbTask() in the second last line, I am getting different output which seems like some internal construct of the mysql library under use.
How can I get dbTask to return the results when called?
Since mysql.query is asynchronous, then you'll have to re-think your architecture.
Instead of having your function return true or false, you'll have to pass in handlers to be called if the query returned true or false.
Something like this:
function dbTask(q, success, failure) {
mysql = new MySQL();
mysql.host = sqlHost;
mysql.user = sqlUser;
mysql.password = sqlPassword;
mysql.query("USE stock");
mysql.query(q, function(err, results, fields) {
if(err) {
console.log("MySQL Error: " + err + ", Query: " + q);
failure(err, results, fields);
} else {
success(results, fields);
}
});
};
Which you'd call like this:
dbTask(q,
function(results, fields) { /* ... */ },
function(err, results, fields) { /* ... */ });
You can't because dbTask returns once the mysql.query call completes to initiate the query. So by the time mysql.query's callback is called, console.log(r) has already executed.
This is the crux of the asynchronous nature of node.js.
What you can do is have dbTask accept a callback parameter that the function calls once results is available so that it can be provided to the caller asynchronously.
Related
I am new to NodeJs and I'm finding the Non Blocking and Asynchronous nature of JS extremely difficult to understand and handle,
I have a piece of code which is supposed to Iterate an array
and for every iteration, I'm supposed to make a DB update.
Can someone provide the correct implementation of Async library functions and help fix my code?
Code example -
function updateFunction(conn, requestBody, callback) {
let arr = [];
async.each(requestBody.arr, function(item, callback) {
let sqlData = []
let columns = "";
if(item.columnData != null){
sqlData.push(item.columnData);
columns += "`columnName` = ?,";
}
if(columns != ''){
columns = columns.substring(0,columns.length-1);
let sqlQuery = 'UPDATE someTable SET '+columns
+' WHERE id = "' + item.id + '"';
conn.query(sqlQuery, sqlData, function (err, result) {
if (err) {
return callback(err, false);
}
})
}
else{
return callback(null, false);
}
columns = "";
sqlData = [];
},
function(err, results) {
//Code never reaches here, don't know why
if (err) {
return callback(err, false);
}
else{
return callback(null, true);
}
});
} // END
During your database query call, on a successful query your callback is not called, therefore causing your code to never reach the final callback.
You will want to add another return statement after your if (err) { return callback(err); } to let async know your database query is finished.
And another thing, according to the docs, the async each method's final callback does not invoke with results in its callback.
A callback which is called when all iteratee functions have finished, or an error occurs. Invoked with (err).
Therefore, it is not required for you to pass a value into the callback statement within your iteratee function.
Modify your code to do this and it will work.
conn.query(sqlQuery, sqlData, function (err, result) {
if (err) {
return callback(err);
}
return callback(null);
})
Hope this helps.
conn.query(sqlQuery, sqlData, async function (err, result) {
if (err) {
return await callback(err, false);
}
})
Something like this.. so the function callback is async here and we gave await which actually waits until the return call is finished..
I am working in Node and trying to load the next sequence from my db. I am able to access the db, load and return the sequence within my function, but I am not able to access it outside of the function.
function getRunId() {
counters.findOne({_id: 'Run_ID'}, function(err, resp) {
if(err) {
console.log(err);
}
console.log('Seq: ' + resp.sequence); // Console Output = Seq: 1234
return resp.sequence;
});
};
var currentRunId = getRunId();
console.log('Run_ID: ' + currentRunId); // Console Output = CRID: undefined
I've checked several pages worth of Stack Overflow issues relating to using callback's, async (node module), how to properly return values in the function, etc... but none of them get me closer to accessing currentRunId outside of the function.
Is this issue further complicated by the use of Mongo queries inside my function?
For anyone stumbling on this later, start by reading this answer.
I've dealt with this a few times so I understand the frustration. You are trying to mix sync and async code by doing this:
var currentRunId = getRunId();
console.log('Run_ID: ' + currentRunId);
The trouble is that console.log('Run_ID: ' + currentRunId) is called immediately after you invoke getRunID() by assigning it to current RunID, and getRunID() resolves after console.log('Run_ID: ' + currentRunId), causing the currentRunId variable to be undefined.
But, you have some options to deal with this. Option one is to return a callback, and log the results of the callback instead. Option 2 is to use an ES6 promise. To use option 2, you need node version 7, and you need to use 'use strict' in your code.
Here are 3 examples built around a function stub that spoofs the results of findOne(). The getRunIdA() is your function, and getRunIdB, and getRunIdC are two example solutions to your current problem.
'use strict'
// A function stub which represents a simplified version of findOne.
// Accepts callback and returns a callback with the results of data
function findOne (callback) {
var data = {
sequence: 6
}
return callback(null, data)
}
// This is a simplified version of your function, which reproduces the undefined result
function getRunIdA () {
findOne(function (err, resp) {
if (err) {
console.log(err)
}
console.log('Seq: ' + resp.sequence)
return resp.sequence
})
}
// This is your function with a callback
function getRunIdB (callback) {
findOne(function (err, resp) {
if (err) {
console.log(err)
}
console.log('Seq: ' + resp.sequence)
return callback(resp.sequence)
})
}
// This is your function with a promise
var getRunIdC = new Promise(function (resolve, reject) {
resolve(findOne(function (err, resp) {
if (err) {
console.log(err)
}
return resp.sequence
}))
})
// Invoke your funciton; get undefined
var currentRunID = getRunIdA()
console.log('Run_ID: ' + currentRunID) // Run_ID: undefined
// Invoke getRunIdB using callback, get 6
getRunIdB(function (id) {
console.log('Run_ID: ' + id) // Run_ID: 6
})
// Invoke getRunIdC with a promise; get 6
getRunIdC.then(function (currentRunID) {
console.log('Run_ID: ' + currentRunID) // Run_ID: 6
})
/*
results for all 3:
Seq: 6
Run_ID: undefined
Seq: 6
Run_ID: 6
Run_ID: 6
*/
Give this a try by saving to your machine and running:
node test.js
Is this issue further complicated by the use of Mongo queries inside my function?
Nope, you just need to pass the results of your query to a promise or a callback so that you can work with the results somewhere else.
I hope this helps!
Edit: OP added the following code in a comment, which I will try to break down and address.
Unfortunately, using getRunIdB results in callback is not defined and using getRunIdC results in currentRunId is not defined
var currentRunID = '';
var getRunId = new Promise(function (resolve, reject) { resolve(counters.findOne({_id: 'Run_ID'}, function (err, resp) {
if (err) {
console.log(err)
}
return resp.sequence;
}))
});
getRunId.then(function (res) {
console.log('Run_ID: ' + res.sequence) // Run_ID: 1234
currentRunID = res.sequence;
})
console.log(currentRunID); // currentRunID is not defined
Check out an answer I gave to a similar question for more details on the JS concurrency model. Simply put, the getRunID() function is executing asynchronous code. What that means is that getRunID() doesn't get inserted into the message queue that determines what order javascript will execute until it's callbacks are completed. Thus, when you log currentRunID outside of the .then() function, the results is undefined because currentRunID is undefined.
I think that ultimately what OP is trying to do is to export the result of the function so that the something can be done with those results, this needs to be done within a callback like so:
getRunId.then(function (res) {
// Do stuff with the run ID here.
})
You are only returning on a callback function but not on the actual function.. Change your code to this:
function getRunId() {
var result = counters.findOne({_id: 'Run_ID'}, function(err, resp) {
if(err) {
console.log(err);
}
console.log('Seq: ' + resp.sequence); // Console Output = Seq: 1234
return resp.sequence;
});
return result; //<-- return result of your function is here
};
var currentRunId = getRunId();
console.log('Run_ID: ' + currentRunId);
I want to write a default callback for HTTP-requests made with superagent. The calls are all made with the async.parallel() framework and the overall result handled together. The callback should handle the results from the HTTP-requests and return a default value, if an error occured. The default value can be specified, but null will be used if it was not set.
I want to construct my handler using a fluent syntax like this:
handle(done).withDefaultValue([]) (empty array is set as default value)
handle(done) (null is used as default value)
I'm relatively new to function currying. Here's what I've tried:
I created a Node module which should be eventually used like this:
My code in handle.js
module.exports = function(done){
this.withDefaultValue = function(defaultValue){
return function(err, result){
if(err){
debug('handling error ' + err + ' and returning default value ' + defaultValue)
return done(null, defaultValue)
}
// sanity check for null and empty objects
result = _.isEmpty(result)?[]:result
done(null, result)
}
}
return this
}
My code in somefile.js
var handle = require('handle')
async.parallel([
function(done){
api.myApiCall(arg1, arg2, handle(done).withDefaultValue([]))
},
function(done){
api.myOtherApiCall(arg1, arg2, handle(done))
}
], function(err, result){
})
Above code works for the first call (the one with withDefaultValue([]), but not for the second call:
Unhandled Error: handle(...).withDefaultValue is not a function
What am I doing wrong?
This seems to do the trick:
console.log = x => document.write(x + "<br>");
function handle(func) {
var handler = function(param) {
console.log('doing stuff...');
func(param);
};
var ret = handler.bind(this, "default");
ret.withDefault = function(val) {
return handler.bind(this, val);
}
return ret;
}
function done(param) {
console.log(param)
}
setTimeout(handle(done));
setTimeout(handle(done).withDefault(123));
This has to be a scope issue that I'm not familiar with. I have a small module I've written as so:
(function () {
var getPlanInfo = function (id, conn) {
conn.query('SELECT * FROM `items` WHERE `id` = ?', [id], function (error, result) {
if (error) console.error('Query error: ' + error.stack);
console.log(result[0]); // Everything is great
return result[0];
});
};
modules.exports.getPlanInfo = function (id, conn) { return getPlanInfo(id, conn); // Typo }
})();
Here comes the problem. When I call it from anywhere (inside the module itself or another file), the return value is always undefined. I checked from within the function, the query returns the result as expected.
var backend = require('./module.js');
var t = backend.getPlanInfo();
t is undefined. This is the same if I call that method from inside the module itself (another function within that module).
I'm familiar with the callback principle in javascript and how objects and functions have to be passed around as an argument to remain in scope. Is this the issue here or is this a node.js particularity?
I tried in in the Developer Console (Chrome), works as expected.
conn.query() looks like it is async. Thus, you can't return its result from getPlanInfo() because getPlanInfo() returns long before the result is available. Returning result[0] from the conn.query() callback just returns a piece of data back into the conn.query() infrastructure. getPlanInfo() has long before already returned.
If you want an async result, then you will have to change getPlanInfo() to use a mechanism that supports getting async results such as a direct callback or a promise or something like that.
Here's a plain callback way:
var getPlanInfo = function (id, conn, callback) {
conn.query('SELECT * FROM `items` WHERE `id` = ?', [id], function (error, result) {
if (error) {
console.error('Query error: ' + error.stack);
callback(error);
return;
}
console.log(result[0]); // Everything is great
callback(0, result[0]);
});
};
modules.exports.getPlanInfo = getPlanInfo;
Then, the caller of that module would look like this:
var m = require('whatever');
m.getPlanInfo(id, conn, function(err, result) {
if (err) {
// error here
} else {
// process result here
}
});
You don't return anything from getPlanInfo. Probably you wanted to write modules.exports.getPlanInfo = function (id, conn) { return getPlanInfo; }
(with return getPlanInfo; instead of return getPlanInfo();)
How can I assign value to variable in global from double callback?
First of, I read some value from file, when its done, I pass it to some fn in callback and want to use result value in initial scope, outside callback.
I can't wrap my head around this for some reason although at first it looks trivial.
var done = function(err, value) {
if (err) {
return;
}
var resultValue = someMethod(value);
};
loadFile(done);
var resultVal = ?? //result value needed here
function loadFile(done) {
fs.realpath(filePath, function (err, resolvedPath) {
if (err) {
return done(err);
}
fs.readFile(resolvedPath, function (err, value) {
if (err) {
return done(err);
}
return done(null, data);
});
});
}
As I said in my comment you are using an asynchronous call to load a file. You want the result of someMethod stored into the global variable resultVal. Except this isn't possible.
When you call loadFile(done) a asynchronous call is made to the server. This call is being resolved by an event. If the event returns 200 the server returned the expected answer. If their is an error it will be passed to done, if not the data will be passed. Let's say this takes about 250 ms to resolve.
In the mean time JavaScript continued parsing the code, because the call was asynchronous, running in a separate thread, thus not halting the execution of the main thread. The next line that gets parsed is returnVal. However the call isn't resolved yet because this line gets executed 1 ms after the function loadFile was called. This leaves a gap of 249 ms.
The solution is to rethink your code to cope with the asynchronous call.
var done = function(err, value) {
if (err) {
return;
}
var resultValue = callBack(value);
};
loadFile(done);
function someMethod(value)
{
//execute whatever you want to do here!
}
function loadFile(done) {
fs.realpath(filePath, function (err, resolvedPath) {
if (err) {
return done(err);
}
fs.readFile(resolvedPath, function (err, value) {
if (err) {
return done(err);
}
return done(null, data);
});
});
}
Of course you can provide the function done with the callback you want. Just look at this code:
var done = function(err, value, callBack) {
if (err) {
return;
}
var resultValue = someMethod(value);
};
loadFile(done, method1);
function method1(value)
{
//execute whatever you want to do here!
}
function loadFile(done, callBack) {
fs.realpath(filePath, function (err, resolvedPath) {
if (err) {
return done(err);
}
fs.readFile(resolvedPath, function (err, value) {
if (err) {
return done(err);
}
return done(null, data, callBack);
});
});
}
Instead of declaring resultValue as : var resultValue = someMethod(value);
You can do global.resultValue = someMethod(value);
This will make resultValue as a global variable.
You can access it anywhere using global.resultValue.
Similarly,instead of using global you can also use process.
global and process are global objects for nodejs just like window is for javascript.