I am trying to get the result back from function where the result is in the callback. However the result always come back as undefined even though I set it to be the res. Can someone explain why this does not work?
function getData(id, callback) {
return dataModel.findOne({ id }).exec((err, res) => {
if (err) return callback(err)
return callback(null, res)
})
}
async function middleware(req.body) {
try {
let result
await getData(req.body, (err, res) => {
if (err) throw err
result = res
})
await nextFunc(result)... // result is undefined
} catch (err) {
console.log(err)
}
}
You are using callbacks, but in order to make things work with async await you are supposed to use promises
Try
function getData(id, callback) {
return new Promise((resolve, reject) => {
dataModel.findOne({ id }).exec((err, res) => {
if (err) reject(err);
resolve(res);
});
});
}
async function middleware(req.body) {
try {
let result = await getData(req.body);
await nextFunc(result);
} catch (err) {
console.log(err);
}
}
Related
I'm trying to wait for a forEach to finish, and the forEach loop has two nested requests inside.
I need to wait untill the forEach finish beacuse I fill an array with the queries results and then, when the forEach is finish, then call another function, but I cannot do it well because sometimes, the array is fully filled, but othertimes the array is incomplete.
Here is my code:
readAllClientsAndInvoices: function(request, response) {
let clientsInvoices = [];
DAOClients.readAllClientesById(request.session.id, function (err, clients) {
if (err) {
console.log(err);
} else {
clients.forEach(function (client, idx, array) {
DAOClients.readClientDataById(client.id, function (err, data) {
if (err) {
console.log(err)
} else {
DAOClients.readAllclientInvoices(data.id, function (err, invoices) {
if (err) {
console.log(err);
} else {
let pair = {
clientData: data,
invoicesList: invoices
};
clientsInvoices.push(pair);
}
});
}
if (idx === array.length - 1) {
DAOClients.createClientPDFReportWOCommentsV2(clientsInvoices, function (err, res) {
if (err) {
console.log(err);
} else {
response.redirect(307, '/client/searchClient');
}
});
}
});
});
}
});
}
This is how I do it now, but I need to wait untill the array is fully filled with all the clients and its invoices and then call to createclientPDFReportWOCommentsV2 function but I don't know how to do it.
Thanks everyone
You can try to use a map instead of forEach in order to accept a return value from every call of the callback function, that return value will have to be a Promise, resolving after particular call has been completed. Since I don't see any particular error handling in your example I just made it so that in case of error Promise resolves undefined which is filtered afterwards in the createClientPDFReportWOCommentsV2 call.
function readAllClientsAndInvoices(request, response) {
DAOClients.readAllClientesById(request.session.id, function (err, clients) {
if (err) return console.log(err);
Promise.all(clients.map(client => {
return new Promise(resolve => {
DAOClients.readClientDataById(client.id, function (err, data) {
if (err) {
console.log(err)
resolve();
} else {
DAOClients.readAllclientInvoices(data.id, function (err, invoices) {
if (err) {
console.log(err);
resolve();
} else {
let pair = {
clientData: data,
invoicesList: invoices
};
resolve(pair);
}
});
}
});
});
})).then(clientsInvoices => {
DAOClients.createClientPDFReportWOCommentsV2(clientsInvoices.filter(Boolean), function (err, res) {
if (err) {
console.log(err);
} else {
response.redirect(307, '/client/searchClient');
}
});
});
});
}
To solve these problems i would use Async/Await https://javascript.info/async-await. Make sure all the methods you're calling on DAOClients returns a Promise https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
For example
function readAllClientesById() {
return new Promise((resolve, reject) => {
// Wait for some data to get fetched from SQL
// and call resolve instead of callback function
resolve(data)
// Or of there was an error
reject(err)
})
}
This is natively supported in the latest versions of Node.js.
Example of Async/Await if promises is implemented:
async function readAllClientsAndInvoices(req, res) {
try {
const clientInvoices = []
const clients = await DAOClients.readAllClientesById(req.session.id)
for (const client of clients) {
const clientData = await DAOClients.readClientDataById(client.id)
const clientInvoices = await DAOClients.readAllclientInvoices(clientData.id)
clientInvoices.push({
clientData: data,
invoicesList: invoices
})
}
// This code won't be executed until the for loop is completed
await DAOClients.createClientPDFReportWOCommentsV2(clientInvoices)
} catch (err) {
return res.status(err.code).send(err)
}
res.redirect(307, '/client/searchClient');
}
I haven't tested the code, it's just an example of how I approach these type of problems.
In order to practice in JS, I'm trying to write the same code using a callback and a promise.
const bcrypt = require('bcrypt');
const saltRounds = 10;
const myPlaintextPassword = 'secret';
Callback version
function hashPassword(password, callback) {
bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) {
return callback(err); // EDIT
}
return callback(null, hash); // EDIT
});
}
hashPassword(myPlaintextPassword, (err, hash) => {
if (err) {
return console.log(err);
}
return console.log(hash);
})
Promise version
async function hashPassword(password) {
return await bcrypt.hash(password, saltRounds);
}
hashPassword(myPlaintextPassword)
.then(res => console.log("result :", res))
.catch(err => console.log("error :", err));
Question : are these snippets "identical" (under the hood) ?
Thank you for your help.
This is a superagent call, I have imported request(i.e is exported from my superagent component class)
How do I use async/await in this for "res.resImpVariable".
request
.post(my api call)
.send(params) // an object of parameters that is to be sent to api
.end((err, res) => {
if(!err) {
let impVariable = res.resImpVariable;
} else {
console.log('error present');
}
});
I reformulated my answer. I think I was misinterpreting before. You could wrap the entire sequence into a Promise-returning function that resolves after the response callback:
function callSuperagent() {
return new Promise((resolve, reject) => {
return request
.post(my api call)
.send(params) // an object of parameters that is to be sent to api
.end((err, res) => {
if(!err) {
console.log('get response', res);
// uncomment this to see the catch block work
// reject('Bonus error.');
resolve(res);
} else {
console.log('error present', err);
reject(err);
}
});
});
}
Then, you can create an async function and await that:
async function doSomething() {
try {
const res = await callSuperagent();
// uncomment this to see the catch block work
// throw 'Artificial error.';
console.log('res', res);
console.log('and our friend:', res.resImpVariable);
} catch (error) {
throw new Error(`Problem doing something: ${error}.`);
}
}
doSomething();
Or if you don't make doSomething, it would be like this:
callSuperagent()
.then((res) => {
console.log('res', res);
console.log('and our friend:', res.resImpVariable);
})
.catch((err) => {
console.log('err', err);
})
I am trying to create a basic API in nodejs to perform CRUD actions on a database.
I do not understand why the result is undefined
exports.getPlaylist = async function (req, res) {
console.log('Recieved getPlaylist request');
result = await connection.executeQuery('SELECT * from Users');
res.status(200).send(result);
console.log(result); // Logs undefined
};
This is the function that gets called and gets the data from the database:
async executeQuery(query) {
connection.query(query, function (err, result) {
if (err) {
console.log('Error executing query: ' + err);
}
console.log(result); // logs correct data
return result;
});
}
You need to "promisify" your callback. (see require('utils').promisify ) for a shorthand for applying to functions that follow node.js callback style (function(err,result){}).
executeQuery(query) {
return new Promise((res, rej) => {
connection.query(query, function (err, result) {
if (err) {
console.log('Error executing query: ' + err);
rej(err)
} else {
console.log(result); // logs correct data
res(result);
}
});
});
}
Now inside an async function you can await executeQuery(query)
The execute is not a promise base function, you cannot use await on a callback function.
change the execute function to promise base
Example:
executeQuery(query) {
return new Promise( (resolve, reject) => {
connection.query(query, function (err, result) {
if (err) {
console.log('Error executing query: ' + err);
reject('Error executing query: ' + err);
}
console.log(result); // logs correct data
resolve(result);
});
});
}
Add try catch in your calling function
exports.getPlaylist = async function (req, res) {
console.log('Recieved getPlaylist request');
try{
result = await connection.executeQuery('SELECT * from Users');
res.status(200).send(result);
console.log(result); // Logs undefined
} catch(error) {
console.log(error)
}
};
I would like to make an Ajax request to my MongoDB server and use the data along with other async tasks using a standalone function so that I can modularize my code as much as possible. I am not very experienced with async programming so I might be doing some basic mistake. In my code, I used another function (doubleAfter2Seconds) returning a promise, which works fine. The result variable from await getMongoData("mydb", url) is outputted as undefined instead of the actual data.
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://{MyServer}:27017/";
function getMongoData(dboArg, urlArg) {
var myPromise = () => {
return new Promise(resolve => {
MongoClient.connect(urlArg, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db(dboArg);
dbo.collection(myCollection).find({}).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
resolve(result[0]);
});
})
})
}
}
function doubleAfter2Seconds(x) {
return new Promise(resolve => {
console.log("v");
setTimeout(() => {
resolve(x * 2);
}, 1000);
});
}
async function addAsync(x) {
// This works
const a = await doubleAfter2Seconds(10);
console.log(a);
// This doesn't work
result = await getMongoData("mydb", url);
console.log(result);
return x;
}
addAsync(10).then((sum) => {
console.log(sum);
});
Here is the corrected getMongoData function based on the comments
function getMongoData(dboArg, urlArg) {
return new Promise(resolve => {
MongoClient.connect(urlArg, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db(dboArg);
dbo.collection(myCollection).find({}).toArray(function(err, data) {
err
? reject(err)
: resolve(data[0]);
});
})
})
}