I have the following code where I need to return an array of objects as a response that has values from the server. The objects are filled with the right values, but when they are pushed into the array they are pushed before. What's the best way of rewriting this asynchronously?
router.post('/teacher-schedule',function(req,res){
var teacher_id= req.body.teacher_id,
school_id= req.body.schoolId,
array = [], tempo = {};
Schedule.find({school_id:school_id}).then(function(schedules){
schedules.forEach(function(element){
ClassSection.findOne({_id:element.class_section_id},function(err,cla){
if(err){
console.log(err);
return res.json({status:"error", message:"error occurred"})
}else{
element.entry.forEach(function(entries){
if( entries.teacher_id==teacher_id){
tempo = {};
console.log("tempo fade"+ tempo)
tempo.class_section = cla.class_title;
tempo.class_section_code = cla.code;
tempo.schedule_id =schedules._id ;
tempo.class_section_id = element._id;
tempo.subject_id = entries.subject_id;
console.log("tempo gabel el subject"+ tempo)
Subject.findOne({_id:entries.subject_id},function(err,subject){
if(err){
console.log(err);
return res.json({status:"error", message:"error occurred"})
}else{
tempo.subject_title = subject.title;
}
})
array.push(tempo);// its always an empty value
}
})
}
})
});
res.json({status:"success",message:"successfully returned", records: array})
}).catch(function(err){
console.log(err);
res.json({status:"error", message:"error occurred"});
})
});
1 Answer 1
Direction
With latest version of Ecmascript we get async/await
that allow us writing asynchronous code in synchronous style.
Consider an example where function f(...)
returns you Promise<F>
, and g(...)
returns Promise<G>
. We can use the following constructions then:
try {
const resultF = await f();
const resultG = await g(resultF);
// keep working with as if we were writing "regular" synchronous code
} catch (anyError) {
console.error(anyError);
}
This code has to be declared in an async
function or arrow function to work. Otherwise, it's still a perfectly asynchronous code...
Sample
In your case, the transformed solution could look similar to this. Please understand this is not 100% working code, because I am not familiar with the particular API of ClassSection
that your code deal with. This is more of a sketch to show the direction.
router.post('/teacher-schedule', async (req, res) => {
const records = [];
try {
const schedules = await Schedule.find({school_id : req.body.schoolId});
schedules.forEach(schedule => {
ClassSection.findOne(
{ _id: schedule.class_section_id},
(classSectionSearchError, classSection) => {
if (err)
throw new Error(classSectionSearchError);
schedule.entry.forEach(entryFound => {
if (entryFound.teacher_id === req.body.teacher_id) {
const tempo = {
class_section: classSection.class_title,
class_section_code: classSection.code,
schedule_id: schedules._id ,
class_section_id: schedule._id,
subject_id: entryFound.subject_id,
};
Subject.findOne(
{ _id: entryFound.subject_id},
(subjectSearchError, subject) =>
{
if (subjectSearchError)
throw new Error(subjectSearchError);
tempo.subject_title = subject.title;
}
);
records.push(tempo);
}
})
})
});
res.status(200).json({ records: records });
} catch (errorCaught) {
console.log(errorCaught);
res.status(500).json({ details: "error occurred: " + JSON.stringify(errorCaught) });
}
});
Bonus
There's whole lot more potential improvements unrelated to asyncronousity, though.
- Use
===
instead of==
, unless you have a strong reason not to follow this advice. - Please setup eslint tool -- it's really hard to read the code which is formatted like that.
- Use
const
for variable declarations. If something needs to get reassigned, uselet
. Do NOT usevar
unless it is necessary (which is super rare the case). - If you're using node.js consider using arrow functions.
- In case of errors set status code to HTTP 500 Internal Server Error, whereas for success use 2XX (they vary depending on scenario).
- Use proper names.
array
is too generic.records
is actually what's being collected. - Use proper names.
cla
name means nothing to a reader... Logging an object right after creation, but before population is meaningless:
tempo = {}; console.log("tempo fade" + tempo)
- Do NOT declare all the variables before actually using them. Always try to achieve joint declaration and assignment:
const recordsFound = []
.
-
1\$\begingroup\$ Thank you for editing the question, however, you shouldn't edit the formatting of questions, and you shouldn't answer invalidate, where editing the question so that it has good formatting, makes your "Please setup eslint tool" somewhat idiotic - as you made it good. \$\endgroup\$2017年10月02日 08:28:21 +00:00Commented Oct 2, 2017 at 8:28
-
\$\begingroup\$ in webstorm arrow functions doesnt work, for example ur code produces the following router.post('/teacher-schedule', async (req, res) => { ^ SyntaxError: Unexpected token ( at Object.exports.runInThisContext (vm.js:78:16) at Module._compile (module.js:543:28) \$\endgroup\$motchezz– motchezz2017年10月02日 16:37:37 +00:00Commented Oct 2, 2017 at 16:37
Explore related questions
See similar questions with these tags.