nodejs callback Error: Can't set headers after they are sent - node.js

i'm new!
I can't cope with the problem!
Did I try adding 'return' or not?
thx!
'GET /edit': async (ctx, next) => {
cache.get('user', function (err, result) {
if (err) {
console.log(err);
return;
}
var user = JSON.parse(result);
if (user == null) {
ctx.render('login.html', {
title: '登录'
});
} else {
ctx.render('edit.html');
}
})
// ctx.redirect('/login');
}

Related

Node Express pass function results outside of forEach

I am attempting to pass results from a forEach function (that needs to be async, but that's a different story) to ejs. Any help is greatly appreciated
router.get('/', (req, res) => {
Block.find({}).populate('servers').exec((err, foundBlocks) => {
if(err){
req.flash('error', 'Block not found. Please try again.');
res.redirect('back');
} else {
MyDB.find({}, (err, foundDB) => {
if(err){
console.log(err)
} else {
foundDB.forEach((DB) => {
pinger(DB.object, (output) => {
if(output){
const string = output;
DB.status = true
console.log(output)
} else {
DB.status = false;
}
})
});
res.render('settings/index', {
blocks: foundBlocks,
});
}
});
}
});
async/await does not works with forEach loop because forEach does not wait for the response to finish, using normal for loop is the key,
your code after replacing forEach with for loop should be like this
router.get('/', (req, res) => {
Block.find({}).populate('servers').exec((err, foundBlocks) => {
if(err){
req.flash('error', 'Block not found. Please try again.');
res.redirect('back');
} else {
MyDB.find({}, (err, foundDB) => {
if(err){
console.log(err)
} else {
for(let DB of foundDB){
pinger(DB.object, (output) => {
if(output){
const string = output;
DB.status = true
console.log(output)
} else {
DB.status = false;
}
})
});
res.render('settings/index', {
blocks: foundBlocks,
});
}
});
}
});

Async loop didn't wait

I am using node-async-loop for asyncronous programming
var array = ['item0', 'item1', 'item2'];
asyncLoop(array, function (item, next)
{
do.some.action(item, function (err)
{
if (err)
{
next(err);
return;
}
next();
});
}, function (err)
{
if (err)
{
console.error('Error: ' + err.message);
return;
}
console.log('Finished!');
});
Like this I am using three async loops one under one.
I want to send the response only after the third inner loop ends. How can I do so?.
Here is the link for node-async-loop (https://www.npmjs.com/package/node-async-loop)
here is my code which i writing but whnever i want to response when the last loop completes it say can set header after send to cliend.
also in console log i am getting data every time when data coming from query.
const id = req.params.id;
finalData = [];
tb_user.findOne({ where: { id: id } }).then((userRiverSys, err) => {
if (userRiverSys) {
// console.log(userRiverSys.regionJson)
asyncLoop(userRiverSys.regionJson, function (item, next) {
// console.log("item", item);
tb_riverSystems.findAll(
{
where: { regionId: item.id }
}).then((findriverSys, err) => {
if (err) {
next(err);
return;
}
// console.log("findriverSys", findriverSys);
if (findriverSys) {
asyncLoop(findriverSys, function (item1, next1) {
if (err) {
next(err);
return;
}
// console.log("item1", item1.dataValues);
tb_facilities.findAll(
{
where: { riverSystemId: item1.dataValues.id }
}).then((findFacilities) => {
if (findFacilities) {
// console.log("findFacilities", findFacilities[0].dataValues.name);
asyncLoop(findFacilities, function (item2, next2) {
if (err) {
next(err);
return;
}
tb_userAccess.findAll(
{
where: { facilityId: item2.dataValues.id }
}).then((userAccessFacilities, err) => {
// console.log("userAccessFacilities", userAccessFacilities[0].dataValues);
// var i = 0;
asyncLoop(userAccessFacilities, function (item3, next3) {
finalData.push({
UserId: item3.userid,
facilityId: item3.facilityId,
})
next3();
},
function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
// i++;
// console.log('Finished!!!!');
// if (userAccessFacilities.length === i) {
// console.log("finalData", i);
// // res.json({"status":"true", "message":"update OrgChallenge"})
// }
})
return res.json({"status":"true", "message":"update OrgChallenge"})
// console.log("finalData", finalData);
})
next2();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!!!');
});
}
});
next1();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!!');
});
}
});
next();
}, function (err) {
if (err) {
console.error('Error: ' + err.message);
return;
}
console.log('Finished!');
});
} else {
console.log("err3", err)
}
})
If you promisify your asynchronous action (so it returns a promise), then you can just use a regular for loop and async/await and there is no need for a 3rd party library to sequence your asynchronous loop. This is modern Javascript:
const { promisify } = require('util');
do.some.actionP = promisify(do.some.action);
async function someFunction() {
const array = ['item0', 'item1', 'item2'];
for (let item of array) {
let result = await do.some.actionP(item);
// do something with result here
}
return someFinalResult;
}
someFunction().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
FYI, in real code, many (or even most) asynchronous operations now offer promisified versions of their API already so usually you don't even need to do the promisify step any more. For example, pretty much all databases already offer a promise interface that you can just use directly.
const loop = async (arr, results = []) => {
const item = arr.shift()
if (!item) {
console.log("DONE");
return results;
}
// as async function
await new Promise(resolve => {
resolve(results.push(`asynced-${item}`))
})
return loop(arr, results);
}
(async () => {
const result = await loop(["item0", "item1", "item2"])
console.log(result);
})();
I'd be happy if I can help you.
but this script uses a recursive function instead of node-async-loop.
so this might not be suitable for you.

how do I return documents back to my API call?

I am trying to call method get_radar_life_cycle from app.get("/api/radar_cloning and it throws the error shown below,
error is coming from line return done(null,documents) how do I return documents back to my API call?
METHODS:-
let get_cloned_radars = function(documents, done) {
let complete_radar_list=[]
for (let i = 0; i < documents.length; i++) {
complete_radar_list.push(documents[i]['orgRadar']);
for (let j = 0; j < documents[i]['clonedRadarsdetailslist'].length; j++) {
complete_radar_list.push(documents[i]['clonedRadarsdetailslist'][j]['clonedRadar']);
}
}
data = complete_radar_list
return done(null, data)
}
let get_radar_life_cycle = function(data,done) {
console.log("data after get_radar_life_cycle")
console.log(data)
Radar_life_cycle.find({orgRadar: {$in:data}})
.then(documents => {
console.log(documents) --> shows correct data
});
return done(null,documents) --> Error is coming from this line
};
API call:
app.get("/api/radar_cloning", (req, res, next) => {
Radar_cloning.find({orgRadar: {$in:req.query.params.split(',')}})
.then(documents => {
get_cloned_radars(documents, function(err,data) {
if (err) {
res.json(err);
if (data!=null){
console.log(data)
}//end for data
}//end of (Err)
});//get_cloned_radars
get_radar_life_cycle(data, function(err,radar_life_cycle_data) {
if (err) {
res.json(err);
console.log(radar_life_cycle_data)
}//end for radar_life_cycle_data
}//end of (Err)
});//end of get_radar_life_cycle
});
});
ERROR:-
(node:10065) UnhandledPromiseRejectionWarning: ReferenceError: documents is not defined
You are trying to access documents outside of its scope, the scope being everything between the { and }. So you cannot access it below the .then(() => {}) scope.
Luckily you are are providing a callback function called done(err, radar_life_cycle_data), which you can use anywhere in the scope of the get_radar_life_cycle(documents, done) function. Even the scopes inside of its scope. When you are calling the done function, what you are basically doing is calling this function (well it has some syntax errors, so I cleaned it up)
function(err,radar_life_cycle_data) {
if (err) {
res.json(err);
console.log(radar_life_cycle_data)
}
}//end for radar_life_cycle_data
which then gets executed
So the solution:
Move your done in your .then(() => {}) scope like this:
let get_radar_life_cycle = function(data,done) {
console.log("data after get_radar_life_cycle")
console.log(data)
Radar_life_cycle.find({orgRadar: {$in:data}})
.then(documents => {
console.log(documents) // --> shows correct data
done(null,documents) // --> No error coming from this line
});
};
Same goes for the data it is not in the scope of the get_cloned_radars
app.get("/api/radar_cloning", (req, res, next) => {
Radar_cloning.find({orgRadar: {$in:req.query.params.split(',')}})
.then(documents => {
get_cloned_radars(documents, function(err,data) {
if (err) {
res.json(err);
if (data!=null) {
console.log(data)
get_radar_life_cycle(data, function(err,radar_life_cycle_data) {
if (err) {
res.json(err);
console.log(radar_life_cycle_data)
} //end of (Err)
}); //end of get_radar_life_cycle
} //end for data
} //end of (Err)
}); //get_cloned_radars
});
But since your code is unreadable, here is a cleaned up version:
app.get("/api/radar_cloning", (req, res, next) => {
const radar_life_cycle_cb = function (err, data) {
if (err) {
res.json(err);
return;
}
console.log(data);
}
const cloned_radar_cb = function (err, data) {
if (err) {
res.json(err);
return;
}
if (data != null) {
get_radar_life_cycle(data, radar_life_cycle_cb);
}
};
Radar_cloning.find({orgRadar: {$in:req.query.params.split(',')}})
.then(documents => get_cloned_radars(documents, cloned_radar_cb));
}

How to multiple fetch data expressJS

I want to display chatbot and facebook data at the same time. how to display it? because when I try to run in the browser but it does not appear anything. I've tried to look it up on stackoverflow but did not get the right reference
route.js
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
CB.getAllRecords( function(e, chatbot) {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : chatbot });
});
FBM.getAllRecords( function(e, facebook) {
res.render('cpanel/facebook', { udata : req.session.user, facebook : facebook });
});
}
});
facebook.js
var facebook = db.collection('facebook');
exports.addNewFacebook = function(newData, callback) {
facebook.findOne({accesstoken:newData.accesstoken}, function(e, o) {
if (o) {
callback('accesstoken-taken');
} else {
facebook.insert(newData, {safe: true}, callback);
}
});
}
exports.getAllRecords = function(callback) {
facebook.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
chatbot.js
var chatbot = db.collection('chatbot');
exports.addNewChatBot = function(newData, callback) {
chatbot.insert(newData, {safe: true}, callback);
}
exports.getAllRecords = function(callback) {
chatbot.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
The easier way to manage asynchronous operations in node.js, especially when you have more than one operation that you want to coordinate is to use Promises instead of plain callbacks. And, fortunately, mongodb supports a promise-based interface for all its asynchronous operations now.
So, first change your methods to return a promise instead of taking a callback:
var chatbot = db.collection('chatbot');
exports.getAllRecords = function() {
return chatbot.find().toArray();
}
var facebook = db.collection('facebook');
exports.getAllRecords = function() {
return facebook.find().toArray();
}
Then, you can use those promises with Promise.all() to coordinate:
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
Promise.all([CB.getAllRecords(), FBM.getAllRecords()]).then(results => {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : results[0], facebook: results[1]});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});
For a call to just a single function that returns a promise, you can use .then():
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
FBM.getAllRecords().then(results => {
res.render('cpanel/facebook', { udata : req.session.user, facebook: results});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});

Update from bd with success but returns undefined on Controller Node.Js

Hy everyone, I'm having some troubles with my rest api. I have in my ui a button where I click to update the state of a bus ( visible / not visible). By clicking on the button I can update the state of the item on the map.
So my problem is when I update the info in my DB in my controller i get the result of this as undefined but the resolve of the db returns
{"command":"UPDATE","rowCount":1,"oid":null,"rows":[],"fields":[],"_parsers":[],"RowCtor":null,"rowAsArray":false}
My return.rows[0] becomes undefined on resolve (I console.log this value and i dont understand why this is happening).
ServicesController.js
ServicesController.prototype.updateMap = function (req, res, next) {
var data = req.body;
if (isEmptyObject(data)) {
res.status(400).send({error: errorMessage.emptyBody});
return;
}
if (data.sn === undefined || data.sn === "") {
res.status(400).send({error: "Invalid serial number"});
return;
}
Database.Services.getDeviceBySn(data.sn).then(function (device) {
var map_data={
"isRoot": data.root,
"visible": data.visible
}
Database.Services.addMapInfo(map_data, device.id).then(function (map) {
console.log("updateMap depois do addMapInfo --- map >>> ", map);
if (map) {
res.status(200).send(map);
} else {
res.status(404).end();
}
}).catch(function (e) {
res.status(500).send(e);
});
}).catch(function (e) {
res.status(500).send(e);
});
}
ServicesDatabase.js
ServicesDatabase.prototype.addMapInfo = function (data, deviceId) {
return new Promise(function (resolve, reject) {
pg.connect(dbStatsConnectionString, function (err, client, done) {
if (err) {
reject(err);
return
}
client.query("UPDATE device_services SET data=jsonb_set(data::jsonb,'{map}',$1::jsonb,true), modified_date=NOW() WHERE device_id=$2", [data, deviceId], function (err, result) {
done();
if (err) {
reject(err);
} else {
resolve(result.rows[0]);
}
});
});
});
}
My parameters are data {"isRoot":"false","visible":"online"} and deviceId "1f110136-9490-4ea5-a46d-3fdfa65ea0ab"
My controller always return 404 because of this
if (map) {
res.status(200).send(map);
} else {
res.status(404).end();
}
Anyone can help me? I dont get it...

Resources