C# async Task vs Js async Task (node js) - javascript

Ok, so I know how to program in C# fairly well and I have started programming in JS recently (node js). To be honest I was in a bot of shock from async calls.
Let's say I have this code in C#:
var t_1 = SomeAsyncTask();
var t_2 = SomeOtherAsyncTask();
Task.WaitAll(t_1, t_2);
var res_1 = t_1.Result;
var res_2 = t_2.Result;
Is there a JS equivalent of this? So far I have managed this:
In User.js:
var express = require("express");
var router = express.Router();
var sqlDo = require("../../js_help/DatabasReq/sqlDo.js");
router.get("/", async function(req, res){
var json = sqlDo.ExecCommand("select * from Users");
res.send(json); //json.recordset
});
module.exports = router;
In sqlDo.js:
module.exports = {
ExecCommand: function(command){
// sql and config are defined before.
sql.connect(config, function () {
var request = new sql.Request();
request.query(command, function (err, recordset) {
if (err) console.log(err)
console.log(recordset.recordset);
return recordset;
});
});
}
};
My problem is that this code is running async. I have tried putting await to different places but nothing worked. So when I start my server it returns nothing. I can tell that it is completing a call because I let it read results into console.
Thanks for any help!
Btw: I have tried googling/stackoverlow-ing,.. But I was not able to find anything that would look like C# equivalent. Is it even possible to write it like in c#? Again thanks for every answer...

To make your ExecCommand function async, you have to make it return a Promise. Read about Promises for instance here
module.exports = {
ExecCommand: function(command){
return new Promise((resolve, reject) => { //return a Promise from the function
sql.connect(config, function () {
var request = new sql.Request();
request.query(command, function (err, recordset) {
if (err) {
reject(err); //if there is an error, reject the Promise
} else {
resolve(recordset); //if no error, resolve the Promise with the result
}
});
});
});
}
};
Depending on your SQL library, it may also support promises already, instead of callbacks
module.exports = {
ExecCommand: function(command) {
return sql.connect(config)
.then(() => {
return new sql.Request().query(command);
})
}
};
or with async/await
module.exports = {
ExecCommand: async function(command) {
await sql.connect(config);
return await new sql.Request().query(command);
}
};
Then you can call this function in the requesthandler either like this
router.get("/", async function(req, res){
try {
var json = await sqlDo.ExecCommand("select * from Users");
res.send(json);
catch (err) {
console.log(err);
res.sendStatus(500);
}
});
or like this
router.get("/", function(req, res){
sqlDo.ExecCommand("select * from Users")
.then(json => { //the promise resolved
res.send(json);
})
.catch(err => { //the promise rejected
res.sendStatus(500);
console.log(err);
});
});
I prefer the second variant. But that may be just my personal opinion ...

Related

ExpressJS MySql Pool Connection Query Return

I am just starting in ExpressJS. So here console.log works, so if I make a call, I do see it in terminal result dataset printed. But, it doesn't return it as response. I do console.log and return but still get no data in response..
index.js
...
app.get("/", async (req, res) => {
let data = await queries();
res.json(data);
});
...
Also tried
...
app.get("/", (req, res) => {
res.json(queries());
});
...
queries.js
function main(sql) {
return pool.getConnection((err, connection) => {
if (err) throw err;
return connection.query(sql, (errt, rows) => {
connection.release(); // return the connection to pool
if (errt) throw err;
console.log(rows);
return rows;
});
});
}
function queries() {
let Q = "SELECT * FROM `items`";
return main(Q);
}
I believe the problem is in the function main() if you do see the result in the terminal. Your functions in the index.js file seems okay. The issue is probably due to the promise not being sent back from the main() function in queries.js file. You need to do 2 things -
Wrap the functionality of the main() in a Promise and return that promise. The callback function of the Promise object takes resolve(use this to return result) and reject(use this to return error).
As the main() will return a promise, you queries() function needs to be async, therefore needs to await the return from the main().
I have attached an Image to show how I implemented this for a DNS-lookup function. You will find some similarities.
// Notice the resolve and reject are functions
function main(sql) {
return new Promise((resolve, reject) => {
pool.getConnection((err, connection) => {
if (err) throw err;
connection.query(sql, (errt, rows) => {
connection.release(); // return the connection to pool
if (errt) throw err;
console.log(rows);
resolve(rows);
});
});
});
}
async function queries() {
let Q = "SELECT * FROM `items`";
let result = await main(Q);
return result;
}
I believe this should work. Give it a try and good luck!

Lambda function not running other async function even with await added to it

I have a Lambda function that isn't running an async function that I am using await on. I am guessing something else is async and I need to await it though I am unsure what can/should be await'ed as documentation for the redis package doesn't talk about promises or async that I saw. I tried putting await in front of the clienthmset... but VS Code says I can't await that. What are my options for getting this to run properly?
Here is the minimum reproducable code I have come up with, note the only things not included in this example are my logger, my imports and my client setup (has password and hostname)
const loadRedis = async (message) => {
client.hmset(`${message.region}:${message._id}`, message, (err, res) => {
if(err) {
logger.error(`Error: ${JSON.stringify(err)}`)
reject(err)
}
if(res) {
logger.info(`Response: ${JSON.stringify(res)}`)
resolve(res)
}
})
}
module.exports.loader = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
let input = JSON.parse(event.Records[0].body);
let message = input.Message
const result = await loadRedis(message)
logger.info(message)
logger.info(result)
let output = result
callback(null, output);
};
The logs in CloudWatch Logs shows that the message log is coming find but the result one is coming back with nothing at all other than the log level.
loadRedis is declared async, but you don't return anything, so it's not really await-ing anything..
Probably you just need to do:
const loadRedis = async (message) => {
return new Promise((resolve, reject) => {
return client.hmset(`${message.region}:${message._id}`, message, (err, res) => {
if(err) {
logger.error(`Error: ${JSON.stringify(err)}`)
reject(err)
}
if(res) {
logger.info(`Response: ${JSON.stringify(res)}`)
resolve(res)
}
})
}
}

Value retrieved from MongoDB is undefined when fetched by Express.js server

Basically what I'm trying to accomplish is pulling the data from a MongoDB database and feed it into my Express.js server. Both of these are ran on the localhost with different addresses. I have tried numerous different methods to get it working and can't seem to get it to work.
Express portion
app.get('/products', function(request, response) {
var query = request.query.keywords;
var promise = new Promise(async (resolve, reject) => {
var productList = await db.getProducts(query);
resolve(productList);
});
promise.then(function(productList) {
console.log(productList); // <--- productList is undefined here
response.json({
productList
});
}).catch('Error retrieving data');
// var productList = Promise.resolve(db.getProducts(query));
// productList.then(products => response.json({products}));
});
MongoDB portion
StoreDB.prototype.getProducts = function(queryParams){
return this.connected.then(function(db){
// TODO: Implement functionality
const collection = db.collection('products');
var productList;
if (!queryParams){
console.log('no queryParams');
collection.find({}).toArray(function(err, docs) {
productList = convertToObject(docs);
// console.log(productList); // <--- productList is defined here
return productList;
});
////////////
Some extra information:
- StoreDB is a functional class with only one property , connected, which is a promise that gets resolved once the connection to the database has been established
- The Express.js server calls getProducts which then resolves the promise
- Within the getProducts call, productList is defined, but when it returns, the value is undefined
With this being said, I thought that by putting the getProducts within in a promise, it would wait for the function call to be complete, then resolve it. However, that is not the case. Some insights would be greatly appreciated. Thanks.
SOLVED
Thanks to #Renan Le Caro
app.get('/products', function(request, response, next) {
db
.getProducts(request.query.keywords)
.then(productList=>response.json({productList}))
.catch(err=>next(err));
});
StoreDB.prototype.getProducts = function(queryParams){
return this.connected.then(db =>
new Promise((resolve, reject) => {
db
.collection('products')
.find({})
.toArray((err, docs) => err ? reject(err) : resolve(docs))
})
}
getProducts is already a Promise; you shouldn't need to be wrapping a Promise in another Promise, and that just opens the code up to more places things can go wrong. But Promise.resolve does not wait for a Promise to resolve; instead, it IMMEDIATELY returns a new Promise that has already resolved with the value you pass it. That's not what you want. And .catch takes a function to run when the Promise fails, not a string.
Your code should be as simple as this:
app.get('/products', function(request, response) {
var query = request.query.keywords;
db.getProducts(query).then(function(productList) {
console.log(productList);
response.json({
productList
});
}).catch(function() {
response.send('Error retrieving data');
});
});
Or, if you prefer the async/await syntax:
app.get('/products', async function(request, response) {
var query = request.query.keywords;
try {
const productList = await db.getProducts(query);
console.log(productList);
response.json({
productList
});
} catch (err) {
response.send('Error retrieving data');
}
});
This is just a promises misuse problem. Here's a fix (not tested).
app.get('/products', function(request, response, next) {
db
.getProducts(request.query.keywords)
.then(productList=>response.json({productList}))
.catch(err=>next(err));
});
StoreDB.prototype.getProducts = function(queryParams){
return this.connected.then(db =>
new Promise((resolve, reject) => {
db
.collection('products')
.find({})
.toArray((err, docs) => err ? reject(err) : resolve(docs))
})
}

await is only valid in async function - nodejs

I'm using node and express to create server for my app. This is how my code looks like:
async function _prepareDetails(activityId, dealId) {
var offerInfo;
var details = [];
client.connect(function(err) {
assert.equal(null, err);
console.log("Connected correctly to server");
const db = client.db(dbName);
const offers_collection = db.collection('collection_name');
await offers_collection.aggregate([
{ "$match": { 'id': Id} },
]).toArray(function(err, docs) {
assert.equal(err, null);
console.log("Found the following records");
details = docs;
});
})
return details;
}
app.post('/getDetails',(request,response)=>{
var Id = request.body.Id;
var activityId = request.body.activityId;
_prepareDetails(activityId,Id).then(xx => console.log(xx));
response.send('xx');
})
While calling getDetails API I am getting
await is only valid in async function error (At line await offers_collection.aggregate)
I am also getting red underline while declaring async function. Node version I'm using is 11.x. I'm also using firebase API. What am I doing wrong here?
You are missing the async declaration on one of your functions. Here is the working code:
async function _prepareDetails(activityId, dealId) {
var offerInfo;
var details = [];
client.connect(async function(err) {
assert.equal(null, err);
console.log("Connected correctly to server");
const db = client.db(dbName);
const offers_collection = db.collection('collection_name');
await offers_collection.aggregate([
{ "$match": { 'id': Id} },
]).toArray(function(err, docs) {
assert.equal(err, null);
console.log("Found the following records");
details = docs;
});
})
return details;
}
app.post('/getDetails', async (request,response)=>{
var Id = request.body.Id;
var activityId = request.body.activityId;
let xx = await _prepareDetails(activityId,Id);
response.send('xx');
})
Await can only be used in an async function, because await is by definition asynchronous, and therefore must either use the callback or promise paradigm. By declaring a function as async, you are telling JavaScript to wrap your response in a promise. Your issue was on the following line:
client.connect(function(err) {
This is where I added the async as mentioned before.
client.connect(async function(err) {
You will notice I made your route use async also, because you would've had an issue before. Notice the two lines in your original code:
_prepareDetails(activityId,Id).then(xx => console.log(xx));
response.send('xx');
Your response would send before you even made your database call because you are not wrapping the response.send within the .then. You could move the response.send into the .then, but if you are going to use async/await, I would use it all the way. So your new route would look like:
app.post('/getDetails', async (request,response)=>{
var Id = request.body.Id;
var activityId = request.body.activityId;
let xx = await _prepareDetails(activityId,Id);
response.send('xx');
})

await promise before callback in async.each

router.post('/runCommand', async function(req, res){
let results = [];
async.each(req.body.requests, async function(request, callback){
const data = await connect(request.command)
await results.push(data);
await callback(null);
}, function(err){
if (!err) {
res.send(202, results)
}
})
})
Res.send is never taking place and callback seems to happen before connect is finished running. Connect is succesfully returning a promise because this
router.get('/topics', async function(req, res) {
console.log('in get');
const data = await connect(req.body.command);
await res.send(data);
});
works fine. But including the async.each to run multiple commands seems broken. I know this is an issue with how I'm calling async.each callback function but research hasn't availed how I should be calling it. Is it possible to use a .then() after awaiting a promise?
function connect(command){
return new Promise(function(resolve) {
let host = {
server: {
host: "host",
port: "port",
userName: "user",
password: config.Devpassword
},
commands: [ command ]
};
var SSH2Shell = require ('ssh2shell'),
//Create a new instance passing in the host object
SSH = new SSH2Shell(host),
//Use a callback function to process the full session text
callback = function(sessionText){
console.log(sessionText)
resolve(sessionText);
}
SSH.connect(callback);
})
};
While you could continue to sink more time into getting async.each() to work, I recommend just dropping it and going exclusively with the async / await syntax which simplifies your code a lot:
router.post('/runCommand', async function (req, res) {
try {
const results = await Promise.all(
req.body.requests.map(({ command }) => connect(command))
);
res.send(202, results);
} catch ({ message, stack }) {
res.send(500, { error: message, stack });
}
})
Looking at the ssh2shell documentation, I think your connect function could be improved as well for better readability and error handling:
const SSH2Shell = require('ssh2shell');
function connect (command) {
return new Promise((resolve, reject) => {
const host = {
server: {
host: 'host',
port: 'port',
userName: 'user',
password: config.Devpassword
},
commands: [command]
};
//Create a new instance passing in the host object
const SSH = new SSH2Shell(host);
SSH.on('error', reject);
SSH.connect(resolve);
});
}
Please feel free to comment if this still doesn't work for you.

Categories

Resources