saving a file when server is terminated - javascript

I'm try to save a content to a file when my node js (express) server is terminated, somehow the file is empty.
function exitHandler() {
//.......
fs.writeFile('graph.txt', Graph, 'utf8', (err) => {
if (err != null) {
console.error(err);
}
});
}
// Bring in our dependencies
const express = require('express');
global.fs = require('fs');
// Connect all our routes to our application.
app = express();
// Turn on the server!
app.listen(3000, () => {
console.log('App listening on port 3000');.
});
// Catches exit event
process.on('exit', exitHandler.bind(null));
// Catches ctrl+c event
process.on('SIGINT', () => {
exitHandler();
process.exit(-1);
});
// Catches "kill pid" (for example: nodemon restart)
process.on('SIGUSR1', exitHandler.bind(null));
process.on('SIGUSR2', exitHandler.bind(null));
// Catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null)
);
If I delete the process.exit(-1) it save content to the file, but never exit the program, and call the event of 'exit'.
thanks

The exitHandler function is asynchronous. You're calling process.exit before the file gets saved. You should use a callback function:
function exitHandler(callback) {
//.......
fs.writeFile('graph.txt', Graph, 'utf8', (err) => {
if (err != null) {
console.error(err);
} else {
callback();
}
});
}
// ...
process.on('SIGINT', () => {
exitHandler(() => process.exit(-1));
});
Or, as schroffl suggested in comments, you can use fs.writeFileSync instead of fs.writeFile:
function exitHandler() {
//.......
fs.writeFileSync('graph.txt', Graph, 'utf8');
}

Related

try catch not working in Node socket.io listener

I am working on an app that involves the use of WebSockets. I have the following code as a part of the application:
io.on('connection', socket => {
let PLAYER = {};
// Listener for event 1
// Listener for event 2
// ...
socket.on('setName', ({name, role, room}) => {
PLAYER.name = name;
PLAYER.role = role;
PLAYER.room = room;
try{
joinRoom(PLAYER); // This function can throw an error
socket.emit('roomJoin');
}catch(e){
socket.emit('error', e.message);
return;
}
});
});
Now, when the joinRoom function does throw an error, Node just crashes with the following exception:
events.js:306
throw err; // Unhandled 'error' event
^
Error [ERR_UNHANDLED_ERROR]: Unhandled error. ('error message')
The error event is never emitted. What has made the try catch block fail?
Note: I am using TypeScript, but have removed type declarations in the above snippet as they shouldn't have anything to do with the problem.
Edit 1:
I have recreated the issue with simpler code as follows:
import express from 'express';
import socketio from 'socket.io';
import http from 'http';
const PORT = process.env.PORT || 8000;
const app = express();
const server = http.createServer(app);
const io = socketio(server);
server.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
const checkLength = (bar) => {
if(bar.length > 14){
throw Error('word is too long!');
}
// ...
}
io.on('connection', socket => {
console.log('new socket connection');
socket.on('foo', ({bar}) => {
try{
checkLength(bar);
}catch(e){
socket.emit('error', e.message);
return;
}
console.log('app did not crash!');
});
}
When the length of bar is greater than 14, the app crashes with the same error mentioned above.
After replicating the server on my local machine, I realized you were calling emit on a reserved keyword for socket.io. When you call socket.emit('error', e.message), there is an expected listener to exist. One like this:
socket.on('error', err => {
// An error occurred.
})
You should use a different phrase for your emit. I personally use oops on my socket servers. socket.emit('oops', {error}). You can read more about error handling on their page.
To add, socket.emit('error') does not pass the error or any data down to the client and instead is handled by the server, which is why you need to use a different phrase.
You can use the promisses, and than you can profite from the then() method which also returns a Promise. It takes up to two arguments: callback functions for the success and failure cases of the Promise.
So your code should be something like this:
import express from 'express';
import socketio from 'socket.io';
import http from 'http';
const PORT = process.env.PORT || 8000;
const app = express();
const server = http.createServer(app);
const io = socketio(server);
server.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
const checkLength = (bar) => {
return new Promise((resolve, reject)=>{
if(bar.length > 14){
// throw Error('word is too long!');
reject('word is too long!')
} else {
resolve('app did not crash!')
}
});
}
io.on('connection', socket => {
console.log('new socket connection');
socket.on('foo', ({bar}) => {
checkLength(bar).then((result) => {
console.log('app did not crash!');
}).catch(err => {
console.log("app did crash with this error:", err);
});
});
}
I noticed you hadn't initialized your PLAYER variable.
let PLAYER = {};
#EDIT 1:
try also adding an error handler for your socket server and log it to get an idea of what is causing the error.
socket.on('error', (error) => {
console.log(error);
});

Having difficulty returning a value from an async function

I'm new to Node and have previously just written Javascript for simple browser extensions. What I'm trying to do is run a shell script and then return the text for it on a get request (simplified version).
I've tried looking at callbacks and can't seem to get my head around it or even adapt another example to what I'm trying to do. My main problem is either that the I'm receiving the error "first argument must be one of type string or buffer. received type undefined" or "received type function" (when I tried to implement a callback, which is what I believe I need to do here?).
I've looked at a few examples of callbacks and promises and seeing them in abstraction (or other contexts) just isn't making sense to me so was hoping someone could help direct me in the right direction?
The code is very crude, but just trying to get some basic functionality before expanding it any further.
var express = require("express");
var app = express();
const { exec } = require("child_process");
var ifcfg = function(callback) {
exec("ifconfig", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return error;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return err;
} else {
var output = stdout.toString();
return callback(output);
}
});
}
app.get("/ifconfig", (req, res) => res.write(ifcfg(data)));
var port = process.env.PORT || 8080;
app.listen(port, function() {
console.log("Listening on " + port);
});
In JavaScript, a callback is a function passed into another function as an argument to be executed later.
Since the command is executed asynchronously you will want to use a callback to handle the return value once the command has finished executing:
var express = require("express");
var app = express();
const { exec } = require("child_process");
function os_func() {
this.execCommand = function(cmd, callback) {
exec(cmd, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}
callback(stdout);
});
}
}
app.get("/", (req, res) => {
console.log("InsideGetss");
var os = new os_func();
os.execCommand('ifconfig', function (returnvalue) {
res.end(returnvalue)
});
});

Error: Can't set headers after they are sent in express-fileupload

app.post('/profile', function(req, res) {
// save file
if (req.files) {
let sampleFile = req.files.sampleFile;
sampleFile.mv('/somewhere/on/your/server/filename.jpg', function(err) {
if (err)
return res.status(500).json(err);
});
}
// do some other stuff
// .............
res.status(200).json(result);
});
I know the problem is caused by return res.status(500).json(err). I can solve the problem by moving res.status(200).json(result) after if (err) block. Since upload file is optional, the user may posting other data without any uploading files. My question is how to send status 200 with a json result after processed other stuff if the solution is
if (err)
return res.status(500).json(err);
res.status(200).json(result);
As was pointed above the problem is you are sending the success reponse outside of the callback.
The solution is to do "other stuff" within the callback.
This should fix the issue -
app.post('/profile', function(req, res) {
// save file
if (req.files) {
let sampleFile = req.files.sampleFile;
sampleFile.mv('/somewhere/on/your/server/filename.jpg', function(err) {
if (err) return res.status(500).json(err);
doOtherStuff();
res.status(200).json(result);
});
} else {
doOtherStuff();
res.status(200).json(result);
}
});
// Write a do other stuff function
function doOtherStuff() {
// do stuff
}
EDIT Adding answer with Promises to avoid code repetition.
function moveFile(file, somePlace) {
return new Promise((resolve, reject)=>{
file.mv(somePlace, function(err) {
if (err) return reject(err);
resolve();
});
});
}
app.post('/profile', function(req, res) {
// save file if present
const fileMovePromise = req.files ?
moveFile(req.files.sampleFile, '/somewhere/on/your/server/filename.jpg')
:
Promise.resolve('No file present');
fileMovePromise
.then(() => {
// do other stuff
})
.catch(err => {
res.status(500).json(err);
});
});
You could use a form of middleware to check if the post is uploading files, the act on the file upload before continuing, have a look at middleware with express here: http://expressjs.com/en/guide/using-middleware.html
With middleware you can do your check to see if the file needs uploading and then call next, otherwise just call next.
app.use('/profile', function (req, res, next) {
if (req.files) {
let sampleFile = req.files.sampleFile;
sampleFile.mv('/somewhere/on/your/server/filename.jpg', function(err, data) {
if (err) {
res.status(500).json(err);
} else {
req.data = data
next()
}
});
}
res.status(200);
}, function (req, res) {
res.status(500).json(req.data);
})
If you are using Node for express, the res param contains res.headersSent, which you can check if the headers have already been sent.
if(res.headersSent) {}
You can also find out a lot more here: https://nodejs.org/api/http.html#http_response_headerssent

kill() function is not recognized for child_process

Using node js I'm calling external script in MATLAB and Python
That is working well using terminal commands to run the scripts using those application ('start')
But when I'm trying to close them using kill() ('stop') I get an error:
TypeError: exec.kill is not a function
I'm using MAC OS and this is my code:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var fs = require('fs');
var path = require('path');
const exec = require('child_process').exec;
var cmd1 =
'/Applications/MATLAB_R2016b.app/bin/matlab -nojvm < /Users/dorsimon/Desktop/liftrack/Testing_Classifier.m';
var cmd2 = 'python /Users/dorsimon/Desktop/liftrack/arduino_sampling_for_workout.py';
app.get('/', function(req, res) {
res.sendfile('index.html');
});
//Whenever someone connects this gets executed
io.on('connection', function(socket) {
console.log('A user connected');
fs.watch('/Users/dorsimon/Desktop/liftrack/result/', function(event, test) {
console.log('event is: ' + event);
fs.readFile('/Users/dorsimon/Desktop/liftrack/result/results.csv', 'utf-8', function read(
err,
data
) {
if (err) {
console.log('err');
throw err;
}
console.log(data);
socket.send(data);
});
});
fs.watch('/Users/dorsimon/Desktop/liftrack/go', function(event, test) {
console.log('event is: ' + event);
fs.readFile('/Users/dorsimon/Desktop/liftrack/go/go.csv', 'utf-8', function read(err, data) {
if (err) {
console.log('err');
throw err;
}
console.log(data);
socket.send(data);
});
});
//Whenever someone disconnects this piece of code executed
socket.on('disconnect', function() {
console.log('A user disconnected');
});
socket.on('start', function() {
exec(cmd1, function(error, stdout, stderr) {
// command output is in stdout
});
exec(cmd2, function(error, stdout, stderr) {
// command output is in stdout
});
});
socket.on('stop', function() {
exec.kill();
});
});
http.listen(3000, function() {
console.log('listening on *:3000');
});
How can I kill those child_process that I started?
Thanks
Dor
You need to store the return value of exec and call kill on that. You can store those values as properties on socket for convenience:
socket.on('start', function() {
socket.child1 = exec(cmd1, function(error, stdout, stderr) {
// command output is in stdout
});
socket.child2 = exec(cmd2, function(error, stdout, stderr) {
// command output is in stdout
});
});
socket.on('stop', function() {
socket.child1.kill();
socket.child2.kill();
});
FWIW, you probably have to do the same with the return values of fs.watch(), and call close() on them when the socket gets closed, otherwise you'll probably run into issues after your server has been running for a while (it creates two watchers for each socket.io connection, but doesn't remove them, so they'll linger).

How to have express handle and capture my errors

var database = require('database');
var express = require('express');
var app = express();
var cors = require('cors');
app.use(cors());
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({
extended: false
});
app.post('/dosomething', urlencodedParser, function(req, res) {
if (!req.body.a) {
res.status(500).send(JSON.stringify({
error: 'a not defined'
}));
return;
}
firstAsyncFunction(req.body.a, function(err, result) {
if (err) {
res.status(500).send('firstAsyncFunction was NOT a success!');
} else {
if (result.b) {
secondAsyncFunction(result.b, function(err, data) {
if (err) {
res.status(500).send('secondAsyncFunction was NOT a success!');
return;
}
res.send('EVERYTHING WAS A SUCCESS! ' + data);
});
}
else {
res.status(500).send('result.b is not defined');
}
}
});
});
function firstAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
function secondAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
var server = app.listen(process.env.PORT || 3000, function() {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
module.exports = app;
I have here a basic express http server. This server has one route, dosomething, which makes two network calls and tells the user if they were a success or not.
This is my entire webserver (this is a bare bones server of my actual server for example purposes). I am now concerned with this server crashing. Reading the docs for express I see there is a default error handler which will catch errors and prevent the server from crashing (http://expressjs.com/en/guide/error-handling.html). I have added the code:
function defaultErrorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}
app.use(defaultErrorHandler);
This still crashes my server though. For example. I had a problem with my database returning an improper JSON response and inside of my firstAsyncFunction (not shown in the code) I tried to parse the JSON and it caused an error telling me it was improper JSON and the server crashed and was unable to take requests anymore until I restarted it. I would like to avoid this and have the default error handler send out a generic response back to the user when this occurs. I thought if I specified the defaultErrorHandler and put it inside of app.use that it would capture and handle all errors, but this does not seem to be the case? Inside of my async function for example you can see I am looking if an error was returned and if it was I send an error back to the user, but what if some other error occurs, how can I get express to capture and handle this error for me?
The defaultErrorHandler cannot handle exceptions that are thrown inside asynchronous tasks, such as callbacks.
If you define a route like:
app.get('/a', function(req, res) {
throw new Error('Test');
});
An error will be thrown, and in this case defaultErrorHandler will successfully catch it.
If the same exception occurs in an async manner, like so:
app.get('/a', function(req, res) {
setTimeout(function () {
throw new Error('Test');
}, 1000);
});
The server will crush, because the callback is actually in another context, and exceptions thrown by it will now be caught by the original catcher. This is a very difficult issue to deal with when it comes to callback.
There is more than one solution though. A possible solution will be to wrap every function that is prone to throw error with a try catch statement. This is a bit excessive though.
For example:
app.get('/a', function(req, res) {
setTimeout(function () {
try {
var x = JSON.parse('{');
}
catch (err) {
res.send(err.message);
}
}, 1000);
});
A nicer solution:
A nicer solution, would be to use promises instead, if it's possible, then for example you can declare a single errorHandler function like so:
function errorHandler(error, res) {
res.send(error.message);
}
Then, let's say you have to following function with fetches stuff from the database (I used setTimeout to simulate async behavior):
function getStuffFromDb() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("{");
}, 100);
});
}
Notice that this function returns an invalid JSON string. Your route will look something like:
app.get('/a', function(req, res) {
getStuffFromDb()
.then(handleStuffFromDb)
.catch(function (error) { errorHandler(error, res) });
});
function handleStuffFromDb(str) {
return JSON.parse(str);
}
This is a very simplified example, but you can add a lot more functionality to it, and (at least theoretically) have a single catch statement which will prevent your server from crushing.

Categories

Resources