router.get('/',ensureLogin,async (req,res)=>{
try{
let result = await Mock.find();
result.forEach(async (e)=>{
if(e.attemptedBy.includes(req.user._id)){
let a_Mock = await User.findOne({_id:req.user._id,"attemptedMock.setNo":e.setNo},{attemptedMock:1});
e.status="Attempted";// not accessible on template as res.render executes first
e.marks = a_Mock.attemptedMock[0].totalMarks; //not accessible
console.log(a_Mock.attemptedMock[0].totalMarks);
}
else{
e.status = "Unattempted";//it is accessible
console.log('else block')
}
})
console.log("second log");
res.render('dashboard',{mocks:result});//I want this code to be executed when the "result" is updated by "forEach" block
}catch(e){
console.log(e);
}
})
Console logs as : 1. "else block" 2. "second log" 3."res.render executed" 4. "total marks", that concludes that res.render executed before "if block's" statement "e.status='Attempted'" so it is not accessible on template page. Please tell me how to refactor in a proper way.
If you are trying to do your loop one at a time and then call res.render() after the loop is done, then the easiest way to do that is to use a regular for loop as it will actually "wait" for the await. .forEach() is not promise-aware so, while the callback itself will wait for the await before it finishes, .forEach() itself will not and it will run the entire loop and then the next line after it will run and then each individual callback will finish sometime later. That will cause you to call res.render() before any of the .forEach() callbacks are done. For this reason, you pretty much never want to use .forEach() with promises as it's just not promise-aware. Instead, use a regular for loop like this:
router.get('/', ensureLogin, async (req,res) => {
try {
let result = await Mock.find();
for (let e of result) {
if (e.attemptedBy.includes(req.user._id)) {
let a_Mock = await User.findOne({_id:req.user._id,"attemptedMock.setNo":e.setNo},{attemptedMock:1});
e.status = "Attempted";
e.marks = a_Mock.attemptedMock[0].totalMarks; //not accessible
} else {
e.status = "Unattempted"; //it is accessible
}
}
console.log("second log");
res.render('dashboard', {mocks:result});
} catch(e) {
console.log(e);
res.sendStatus(500);
}
});
Note also that this code sends an error status in the catch block. You need to always send some response to the incoming http request.
inside for each, you can wrap everything in function.
refernece :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all#:~:text=The%20Promise.,input%20iterable%20contains%20no%20promises.
processE = async(e)=>{
try{
if(e){
await something
e.something 1= somevalue
}else{
e.something 2= somevalue
}
return false
}catch(err){
//you can also throw depends on how u handle your err and type of promise.something you use
return err
}
}
let promises = []
e.forEach( (e)=>{
promises.push(processE(e))
})
/// Promise.something you can take a look at reference and use ethe one behavior that you prefer.
const result = await Promise.all(promises)
//render here
Related
One of the promises is not being resolved, what can be the issue possibly?
const items = await Promise.all(data.map(async i => {
const tokenUri = await contract.tokenURI(i).catch(function (error) {return;});
if (tokenUri.length < 8) {
return;
}
let url = "http://127.0.0.1:5001/api/v0/get?arg=" + tokenUri.slice(7)
const meta = await axios.post(url).catch(function (error) {return;});
success++;
}), function (err) {
callback(err);
})
This part of the code is misplaced:
, function (err) {
callback(err);
})
Currently, it is being passed as a second argument to Promise.all(). Promise.all() only takes one argument (an iterable like an array) so that's being ignored and that callback will NEVER get called.
If you meant for that to be like the second argument to .then(), then you'd have to have a .then() to use it with, but you're trying to use it as second argument to Promise.all() and that is not correct.
If you want to know when Promise.all() is done, you would do this:
try {
const items = await Promise.all(data.map(async i => {
const tokenUri = await contract.tokenURI(i).catch(function(error) { return; });
if (tokenUri.length < 8) {
return;
}
let url = "http://127.0.0.1:5001/api/v0/get?arg=" + tokenUri.slice(7)
const meta = await axios.post(url).catch(function(error) { return; });
success++;
}));
console.log("Promise.all() is done", items);
} catch (e) {
console.log(e);
}
Note, you are silently "eating" errors with no logging of the error inside this loop which is pretty much never a good idea.
If, as you propose, one of your promises is actually never being resolved/rejected, then none of this code will do anything to fix that and you will have to go down to the core operation and find out why it's not resolving/rejecting on its own. Or, configure a timeout for it (either in the API or by manually creating your own timeout).
The code below is an example of what may take place during development.
With the current code, the outer function may throw an error but in this case wont. However, the nested function WILL throw an error (for examples sake). Once it throws the error it cannot be caught as it is asynchronous function.
Bungie.Get('/Platform/Destiny2/Manifest/').then((ResponseText)=>{
//Async function that WILL throw an error
Bungie.Get('/Platform/Destiny2/Mnifest/').then((ResponseText)=>{
console.log('Success')
})
}).catch((error)=>{
//Catch all errors from either the main function or the nested function
doSomethingWithError(error)
});
What I want is for the outer most function to catch all asynchronous function error's but with this code I cannot. I have tried awaiting the nested function but there may be certain circumstances where it will be quicker to not wait for the function. I also tried to include a .catch() with each nested function but this would require a .catch() for each function that would allhandle the error in the same way e.g. doSomethingWithError().
you only needs return the inner function in the outside function.
see example below:
const foo = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo'), 1000);
});
foo.then((res)=>{
console.log(res)
return new Promise((resolve,reject)=>{
setTimeout(() => reject("bar fail"), 1000);
})
}).catch((e)=>{
// your own logic
console.error(e)
});
this is called promise chaining. see this post for more info https://javascript.info/promise-chaining
if you have multiple promises can do something like:
const foo1 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo1'), 1000);
});
const foo2 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo2'), 2000);
});
const foo3 = new Promise((resolve,reject) =>{
setTimeout(() => reject('foo3'), 3000);
});
const bar = new Promise((resolve,reject) =>{
setTimeout(() => resolve('bar'), 4000);
});
foo1
.then((res)=>{
console.log(res)
return foo2
})
.then((res)=>{
console.log(res)
return foo3 // throws the error
})
.then((res)=>{
console.log(res)
return bar
})
.catch((e)=>{
// every error will be cached here
console.error(e)
});
I would aim to use async / await unless you have very particular reasons, since it avoids callback hell and makes your code simpler and more bug free.
try {
const response1 = await Bungie.Get('/Platform/Destiny2/Manifest/');
const response2 = await Bungie.Get('/Platform/Destiny2/Mnifest/');
console.log('Success');
} catch (error) {
doSomethingWithError(error);
}
Imagine each Bungie call takes 250 milliseconds. While this is occurring, NodeJS will continue to execute other code via its event loop - eg requests from other clients. Awaiting is not the same as hanging the app.
Similarly, this type of code is used in many browser or mobile apps, and they remain responsive to the end user during I/O. I use the async await programming model in all languages these days (Javascript, Java, C#, Swift etc).
Try this:
let getMultiple = function(callback, ... keys){
let result = [];
let ctr = keys.length;
for(let i=0;i<ctr;i++)
result.push(0);
let ctr2 = 0;
keys.forEach(function(key){
let ctr3=ctr2++;
try{
Bungie.Get(key, function(data){
result[ctr3] = data;
ctr--;
if(ctr==0)
{
callback(result);
}
});
} catch(err) {
result[ctr3]=err.message;
ctr--;
if(ctr==0)
{
callback(result);
}
}
});
};
This should get all your data requests and replace relevant data with error message if it happens.
getMultiple(function(results){
console.log(results);
}, string1, string2, string3);
If the error causes by requesting same thing twice asynchronously, then you can add an asynchronous caching layer before this request.
I'm refactoring my nodejs code. I'm implementing transactions using node-postgres. My code now looks like this:
const controller = async (req, res, next) => {
const client = await pool.connect()
try {
// get the user ID out of the params in the URL
const { user_id } = req.params
let query, values, result
try {
await client.query('BEGIN')
} catch (err) {
throw new CustomError(err)
}
try {
query = 'QUERY_1'
values = ['VALUES_1']
result = await client.query(query, values)
} catch (err) {
throw new CustomError(err)
}
// handle some stuff
try {
query = 'QUERY_2'
values = ['VALUES_2']
result = await client.query(query, values)
} catch (err) {
throw new CustomError(err)
}
// handle some more stuff
try {
await client.query('COMMIT')
} catch (err) {
throw new CustomError(err)
}
return res.status(200).json({ response: 'ok' })
} catch (err) {
await client.query('ROLLBACK')
return next(new CustomHandleError(400, 'something_went_wrong'))
} finally {
client.release()
}
}
This code works, but I have some questions. I'm a beginner at nodejs.
1) In my 'finally' block, I release the client back to the pool. But when everything is OK, I return the response in the 'try' block. Online I read that the 'finally' block is ALWAYS executed, so is it OK to return a (good) response in the try block and releasing the client in the finally block?
2) Is it OK (or is it anti-pattern) to nest multiple try catch blocks. The reason is that the node-postgres throws errors but I want to return all errors to a custom error handler, so I catch those errors first and then throw them again in my CustomError handler.
Thanks in advance!
You can significant simplify your try/catch handling since all the inner catch blocks all do the same thing and are not necessary:
const controller = async (req, res, next) => {
const client = await pool.connect()
try {
// get the user ID out of the params in the URL
const { user_id } = req.params
let query, values, result;
await client.query('BEGIN');
query = 'QUERY_1'
values = ['VALUES_1']
result = await client.query(query, values)
// handle some stuff
query = 'QUERY_2'
values = ['VALUES_2']
result = await client.query(query, values)
// handle some more stuff
await client.query('COMMIT')
return res.status(200).json({ response: 'ok' })
} catch (err) {
await client.query('ROLLBACK')
return next(new CustomHandleError(400, 'something_went_wrong'))
} finally {
client.release()
}
}
Then, to your questions:
1) In my 'finally' block, I release the client back to the pool. But
when everything is OK, I return the response in the 'try' block.
Online I read that the 'finally' block is ALWAYS executed, so is it OK
to return a (good) response in the try block and releasing the client
in the finally block?
Yes, this is a good use of finally.
2) Is it OK (or is it anti-pattern) to nest multiple try catch blocks. The reason is that the node-postgres throws errors but I want to return all errors to a custom error handler, so I catch those errors first and then throw them again in my CustomError handler.
It's OK (not an anti-pattern when it achieves a specific goal), but in this case it is not necessary because all your inner catch() blocks all do the same thing and are all just caught by your outer catch block so you can just keep the outer catch and get rid of all the inner ones. All your await statements will just go directly to your outer catch if they reject in my code above which all you were doing anyway with all your inner catch statements so they were redundant.
Some reasons for needing the inner catch are:
You want to create a custom error (that is different for each async operation) that you will actually use in the final result of the function.
You want to "handle" an error locally and continue processing down a different code path that is not just an immediate error return. For example, you attempt to load a config file, catch the error and just proceed with the rest of the code in your function with defaults if the config file is not present.
1) Yes, this is good practice.
2) I don't think it's automatically an anti-pattern, but I'd avoid it if I can find a cleaner way to do it.
Rule of thumb from Robert C. Martin's suggestion from his book 'Clean Code':
if the keyword 'try' exists in a function, it should be the very first word in the function and that there should be nothing after the catch/finally blocks.
If you have a chance, then avoid nesting try-catches.
Blocks out into separate functions
I'v been searching around for a few hours (with no success) on how to have an async function return a result, store it in a variable outside the function and reuse that data.
My issue is that I fetch the same data over and over in a few of my functions which seems unnecessary.
Basically this is what I want, and right now it's returning a promise.
let leads;
try {
var result = await fetch('http://localhost:3000/lds');
leads = await result.json();
return leads;
} catch (e) {
// handle error
console.error(e)
}
}
var results = readDb();
console.log(results);
For example, initially I run a function fetch the data and create a table.
Secondly I run another function that fetches the same data to create pagination buttons.
Thirdly I run another function that fetches the same data, yet again, and listens for the pagination button click to show the data of the corresponding page number.
Ideally I would fetch the data only once.
Thanks in advance!
We can define leads outside the function and check if it's necessary to fetch it:
let leads;
const getLeads = async function() {
try {
if (!leads) { // fetch leads only they weren't already loaded
const result = await fetch('http://localhost:3000/lds');
leads = await result.json();
}
return leads;
} catch (e) {
// handle error
console.error(e)
}
}
Since you are using async/await you need to await the value, however it is only available in an async function. You can reuse a variable outside the function to store the value once it is loaded.
// reuse leads variable
let leads;
// async function to populate/return leads
async function readDb() {
// only populate if it is undefined
if (typeof leads === 'undefined') {
const result = await fetch('http://localhost:3000/lds');
leads = await result.json();
}
// return just loaded or referenced value
return leads;
}
// call the function asynchronously
(async function(){
// await the results here inside an async function
const results = await readDb();
console.log(results);
}());
How to handle multiple calls to the same function when its returning nothing. I need to wait untill all calls are finished so i can call another function.
For now I'm using Promise.all() but it doesn't seem right:
Promise.all(table_statements.map(i => insertValues(i)))
.then(function(result) {
readNodeData(session, nodes);
})
.catch(function() {
console.log(err);
})
function insertValues(statement) {
return new Promise((res, rej) => {
database.query(statement, function (err, result) {
if (err) {
rej(err)
}
else{
console.log("Daten in Tabelle geschrieben")
res(); // basically returning nothing
}
});
});
}
This writes data to a database in multiple statements, i need to wait untill all are finished.
Is this actually the "right" way to do it? I mean... it works, but i have the feeling it's not how you are supposed to do it.
Using Promise.all for your case is a good call, since it returns a Promise, when all the promises passed as an iterable are resolved. See the docs.
However, for brevity and readability, try converting your insertValues into async-await function as follows. This tutorial would be a great place to start learning about async functions in JavaScript.
// async insertValues function - for re-usability (and perhaps easy unit testing),
// I've passed the database as an argument to the function
async function insertValues(database, statement) {
try {
await database.query(statement);
} catch (error) {
console.error(error);
}
}
// using the insertValues() function
async function updateDatabase(database) {
try {
// I am using 'await' here to get the resolved value.
// I'm not sure this is the direction you want to take.
const results = await Promise.all(
tableStatements.map(statement => insertValues(database, statement))
);
// do stuff with 'results'.. I'm just going to log them to the console
console.log(results);
} catch (error) {
console.error(error);
}
}
Here, insertValues() function doesn't return any value. Its operation on the database is entirely dependent on the query statement passed to it. I wrapped it within a try-catch block so as to catch any errors that might arise while performing the operation (s) above. More details on handling errors using try-catch can be found here.
Your promisified write to database looks ok, so we can update code from another part.
Let's rewrite it a little to use async/await and try/catch.
(async() => {
const promisifiedStatements = table_statements.map(i => insertValues(i));
try {
await Promise.all(promisifiedStatements);
readNodeData(session, nodes);
} catch(e){
console.log(e)
}
})();
I use here IIFE to use await behaviour.