How to end a script at any given time in nodejs - javascript

I am working on a small chat app backend in NodeJS.
I am trying to make a plugin system for it, Is there some way that I can end the script any time I want.
My code
pluginLoadder.js
/**
* Starts to load plugins
* #param {function} cb Runs after all plugins are loaded
*/
function startPlugins(cb) {
fs.readdir(pluginPath, function(err, files) {
if (err !== null) {
console.error(err);
} else {
const pluginFileREGEXP = /.+\.js$/;
files = files.filter(function(value) {
return pluginFileREGEXP.test(value);
});
files.forEach(function(val) {
try {
const tempPlugin = require(pluginPath +'/'+ val );
const func = {
events: events,
log: log,
};
tempPlugin.main(func);
events.emit('test');
} catch (e) {
if (e instanceof TypeError) {
log(`ERROR: ${val} is not a talker-backend plugin\n
E: ${e}`, 'error');
} else {
log(e, 'error');
}
}
});
cb();
}
});
}
./plugins/spamer.js
//* This is a plugin thats in the directory ./plugins
/* I want to stop this script through pluginLoader.js
*/
function main(func) {
const {events, log} = func;
setInterval(function (){
log('test', 'event');
}, 1000)
}
module.exports = {
main: main
};

In most cases you could use process.exit which takes in an exit code as an integer parameter (Node.js interprets non-zero codes as failure, and an exit code of 0 as success).
Then if you want to specifically target a process (assuming you are running multiple ones) you could use process.kill which takes process pid as a parameter.
Finally you could use process.abort which immediately kills Node.js without any callback executed and with a possibility to generate a core file.
To sum up:
// You would mostly use
process.exit(exitCode); // where exitCode is an integer
// or
process.kill(processPid);
// or
process.abort();
Just call it anytime you need to exit your program in your code.

Related

How to prematurely finish a pipeline within a JS transform

Problem: I am dealing with large files (>10GB).
Sometimes I want to process the complete files and sometimes I just want to sample a few lines.
The processing setup is a pipeline:
pipeline(
inStream,
split2(),
sc,
err => {
...
}
);
sc is a transform that essentially counts some flags in the file.
The code works fine when processing the complete file but never produces the output in ... if I want to exit from the transform before inStream has finished.
_transform(chunk,encoding,done) {
let strChunk = decoder.write(chunk);
if(strChunk === '\u0003') {
this.push('\u0003');
process.exit(0);
}
if(strChunk.startsWith("#")) {
done();
} else {
if(this.sampleMax === 0) {
this.push('\u0003');
//process.exit(0);
} else
if(this.sampleMax > 0)
this.sampleMax--;
let dta = strChunk.split("\t");
let flag = dta[1].trim();
this.flagCount[flag]++;
done();
}
if I use //process.exit(0), the code in the pipeline following sc is not reached.
if I only use this.push('\u0003'); the complete inStream is processed.
The question is how to properly terminate the transform and continue with the downstream pipeline without completely reading inStream.
One solution would be to throw an error or create an error implicitly by destroying the stream. Both options are shown in the code below.
_transform(chunk,encoding,done) {
let strChunk = decoder.write(chunk);
if(strChunk === '\u0003') {
this.push('\u0003');
process.exit(0);
}
if(strChunk.startsWith("#")) {
done();
} else {
if(this.sampleMax === 0) {
//Either of the following lines can solve the problem.
throw("PLANNED_PREMATURE_TERMINATION");
this.destroy(); //=> Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close
} else
if(this.sampleMax > 0)
this.sampleMax--;
let dta = strChunk.split("\t");
let flag = dta[1].trim();
this.flagCount[flag]++;
done();
}
The next step is to react to the errors generated in the above code in the pipeline.
pipeline(
inStream,
split2(),
sc,
err => {
if(err && (err === "PLANNED_PREMATURE_TERMINATION") ||
(err === "Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close") {
//do whatever should happen in this case
} else {
//stream was completely processed
//do whatever should happen in this case
}
}
);
Since err contains the message that was thrown during premature termination, we can react to that specifically and present the partially aggregated data in sc. This solved the initial problem and seems to be the only obvious route in this situation. Therefore, I post this solution. Would be great to have other (more elegant) solutions.

array of callbacks as Promise?

I am working through some old legacy code dealing with network requests using RPC/YUI library. It essentially creates tags to handle network requests. There are no promises for these.Also, because of IE11 support, we cannot use the native Promise object. Our build process does not utilize any NPM dependencies, so we cannot use any babel related polyfills.
There is a bug I am working on to fix that the argument ignoreError gets overwritten each time another function calls the same function....obviously! We have multiple functions calling this network request function library. Sometimes we want to ignore an error, sometimes we do not.
What is the ideal way to store the multiple requests made and their respective error callbacks so the appropriate item is called?
example:
var rpcUrl,
rpcRetries,
rpcIgnoreError;
// main function that sets some globals:
rpc: function(url, retries, ignoreError) {
rpcUrl = url;
rpcRetries = retries;
rpcIgnoreError = ignoreError;
this.doRpc();
},
// calls the YUI library to initialize network script:
doRpc: function() {
YAHOO.util.Get.script(rpcUrl, {
onFailure: function() {
this.callbackError(true);
},
timeout: 55000
});
},
// YUI callback
callbackError: function(retry) {
if (retry && rpcRetries > 0) {
rpcRetries = rpcRetries - 1;
this.doRpc();
} else {
// ** how do i know this error handling is for the script which failed?
if (!rpcIgnoreError) {
this.populateFormStatus(6);
}
}
},
now, we have multiple functions calling rpc() such as:
sendConfig: function() {
this.rpc(urlForEndpoint, 3, true);
},
sendUser: function() {
this.rpc(urlForEndpoint, 3, false);
},
sendWidget: function() {
this.rpc(urlForEndpoint, 3, false);
},
I am concerned making an array of callbacks will not guarantee that each item is handled with its respective handler.
I could do something like create a map constant:
var RPC_ERR_CB = {
sendConfig: false,
sendUser: true,
sendWidget: true
};
// and then in the onFailure callback, I can read the src of the script tag:
...
doRpc: function() {
YAHOO.util.Get.script(rpcUrl, {
onFailure: function() {
var hasCB = Object.keys(RPC_ERR_CB).some(function(item) {
return arguments[0].src.indexOf(RPC_ERR_CB[item]) <= 0;
});
if (hasCB) {
this.callbackError(true);
}
},
timeout: 55000
});
},
Hope this makes sense...THANKS!
You could pass the values into doRpc, then you can pass it to callbackError or handle it in doRpc (like your example code at the end). This will prevent the global variable from changing on you.
If you're not able to use Promises or ES6 Classes, your options become somewhat limited. If at all possible, I would recommend biting the bullet on getting a Babel transpilation process so you can take advantage of newer features without needing to drop IE11 support.
As it is now though, ideally you don't want to track every request in a global variable somewhere. You can handle each transaction independently by creating each request as a self-contained object:
function RpcRequest (url, retries, ignoreError) {
this.url = url
this.retries = retries
this.ignoreError = ignoreError
}
RpcRequest.prototype.send = function() {
YAHOO.util.Get.script(this.url, {
onFailure: function() {
this.callbackError(true);
},
timeout: 55000
});
}
RpcRequest.prototype.callbackError = function(retry) {
if (retry && this.retries > 0) {
this.retries = this.retries - 1;
this.send();
} else {
if (!this.ignoreError) {
// ...
}
}
}
// Somewhere else, initiate a request
var requestOne = new RpcRequest("http://blah", 3, false)
requestOne.send()
Something I noted when looking over your code: the code that's creating the request has no idea whether the request succeeded or not. And when you have an error, the calling context doesn't know anything about that error. I took a look at the library you mentioned, and it does appear to have some context that you can pass along.
If I were to rewrite this a little bit, I'd do something like this to bubble the error up to your calling context:
RpcRequest.prototype.send = function(callback) {
YAHOO.util.Get.script(this.url, {
onFailure: function(context) {
if( this.ignoreError ) {
context.ignoredError = true
callback(null, context);
return;
}
var retError = new Error('Failure doing something!');
retError.context = context;
callback(retError);
},
onSuccess: function(context) {
callback(null, context);
},
timeout: 55000
});
}
// Somewhere else in the code...
sendWidget: function() {
var request = new RpcRequest(urlForEndpoint, 3, false)
request.send(function(err, result) {
if( err ) {
console.error('Failed at doing a widget thing:', err.context);
// maybe even:
// throw err;
return;
}
if( result.ignoredError ) {
console.warn('Ignored an error on the widget thing:', result);
return;
}
console.log('Success on the widget thing!', result);
})
}

NodeJS infinite running terminal app with read from terminal

I am trying to create a terminal app that will run indefinitely and will have the ability to read from the terminal.
I tried to user the "readline" api but the app terminates without waiting for any input.
I added a "while(true)" loop but it seems that the thread gets stacked in the loop and does not respond to my input.
I need a series of random numbers.
To accomplice it I added an interval of 1000ms and the result was the same with while loop.
To summary I need to create an app that reads from the terminal and create random numbers on a given interval.
Any guidance will be appreciated.
Edit 1
Additional information I just thought to give you.
I tried to put either the readline call or the interval in a separate forked process but nothing changed.
Also I tried to use recursion for the readline.
Edit 2
Although I accepted #amangpt777`s answer I would like to give another problem that you might encounter.
I was calling my script like this 'clear | node ./script.js' on windows` powershell.
I believe that it was the pipe that was blocking my input.
I don't know if this can happen on linux, I haven't tested it.
I just add it here so you keep it in mind.
I am not sure what you are trying to accomplish here. But following code will take input from user using readline and will keep on storing the input in an array. Note that I have some commented code in this which can be uncommented if you want a publish subscriber model. Also that you will need to add more code to sanitize and validate your input. I hope you will get some pointers to achieve what you want with this:
var readline = require('readline');
//var redis = require('redis');
//let subscriber = redis.createClient();
//let publisher = redis.createClient();
let numEntered = [];
var r1 = readline.createInterface(
{
"input": process.stdin,
"output": process.stdout
}
);
// subscriber.subscribe('myFunc');
// subscriber.on('message', (channel, msg) => {
// //Your logic
// });
function printMyArr(){
console.log("Numbers entered till now: ", numEntered);
}
function askNumber(){
askQuestion('Next Number?\n')
.then(ans => {
handleAnswer(ans);
})
.catch(err => {
console.log(err);
})
}
function handleAnswer(inputNumber) {
if(inputNumber === 'e') {
console.log('Exiting!');
r1.close();
process.exit();
}
else {
numEntered.push(parseInt(inputNumber));
//publisher.publish('myFunc', parseInt(inputNumber));
// OR
printMyArr();
askNumber();
}
}
function askQuestion(q) {
return new Promise((resolve, reject) => {
r1.question(q, (ans) => {
return resolve(ans);
});
});
}
function init() {
askQuestion('Enter Stream. Press e and enter to end input stream!\n')
.then(ans => {
handleAnswer(ans);
})
.catch(err => {
console.log(err);
})
}
init();

HTTP requests until header has attachment

We have a web application which upon visiting the url, will prepare and generate a .zip file which is then downloaded.
I need to create a nodejs application using requestjs that can keep making requests until there is an attachment header, from which point it would be downloaded.
The page which generates the .zip file contains a simple html message, stating that the file is being prepared for download. With a javascript reload(true) function called on load.
I'm not sure if this is the right way of doing it, but I am open to suggestions.
You could use async.until to loop through some logic until the header is available:
let success = true;
async.until(
// Do this as a test for each iteration
function() {
return success == true;
},
// Function to loop through
function(callback) {
request(..., function(err, response, body) {
// Header test
if(resonse.headers['Content-Disposition'] == 'attatchment;filename=...') {
response.pipe(fs.createWriteStream('./filename.zip'));
success = true;
}
// If you want to set a timeout delay
// setTimeout(function() { callback(null) }, 3000);
callback(null);
});
},
// Success!
function(err) {
// Do anything after it's done
}
)
You could do it with some other ways like a setInterval, but I would choose to use async for friendly asynchronous functionality.
EDIT: Here's another example using setTimeout (I didn't like the initial delay with setInterval.
let request = require('request');
let check_loop = () => {
request('http://url-here.tld', (err, response, body) => {
// Edit line below to look for specific header and value
if(response.headers['{{HEADER_NAME_HERE}}'] == '{{EXPECTED_HEADER_VAL}}')
{
response.pipe(fs.createWriteStream('./filename.zip')); // write file to ./filename.zip
}
else
{
// Not ready yet, try again in 30s
setTimeout(check_loop, 30 * 1000);
}
});
};
check_loop();

Fill Azure Mobile Services from Scheduler

I created code like this for getting news from xml export from another website and I am trying to fill with it my database.
function UpdateLunchTime() {
var httpRequest = require('request');
var xml2js = require('xml2js');
var parser = new xml2js.Parser();
var url = 'http://www...com/export/xml/actualities';
httpRequest.get({
url: url
}, function(err, response, body) {
if (err) {
console.warn(statusCodes.INTERNAL_SERVER_ERROR,
'Some problem.');
} else if (response.statusCode !== 200) {
console.warn(statusCodes.BAD_REQUEST,
'Another problem');
} else {
//console.log(body);
parser.parseString(body, function (err2, result) {
//console.log(result.Root.event);
var count = 0;
for (var i=0;i<result.Root.event.length;i++)
{
//console.log(result.Root.event[i]);
InsertActionToDatabase(result.Root.event[i]);
}
/*
result.Root.event.forEach(function(entry) {
InsertActionToDatabase(entry);
});
*/
});
}
});
}
function InsertActionToDatabase(action)
{
var queryString = "INSERT INTO Action (title, description, ...) VALUES (?, ?, ...)";
mssql.query(queryString, [action.akce[0], action.description[0],...], {
success: function(insertResults) {
},
error: function(err) {
console.log("Problem: " + err);
}
});
}
For individual actualities it's working fine but when I run it over whole xml I get this error:
Error: [Microsoft][SQL Server Native Client 10.0][SQL Server]Resource ID : 1. The request limit for the database is 180 and has been reached. See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance.
And for a few last objects I get this error:
Error: [Microsoft][SQL Server Native Client 10.0]TCP Provider: Only one usage of each socket address (protocol/network address/port) is normally permitted.
Thanks for help
The problem is that you're trying to make too many concurrent (insert) operations in your database. Remember that in node.js (almost) everything is asynchronous, so when you call InsertActionToDatabase for one of the items, this operation will start right away and not wait before it finishes to return. So you're basically trying to insert all of the events at once, and as the error message said there's a limit on the number of concurrent connections which can be made to the SQL server.
What you need to do is to change your loop to run asynchronously, by waiting for one of the operations to complete before starting the next one (you can also "batch" send a smaller number of operations at once, continuing after each batch is complete, but the code is a little more complicated) as shown below.
var count = result.Root.event.length;
var insertAction = function(index) {
if (index >= count) return;
InsertActionToDatabase(result.Root.event[i], function() {
insertAction(index + 1);
});
}
insertAction(0);
And the InsertActionToDatabase function would take a callback parameter to be called when it's done.
function InsertActionToDatabase(item, done) {
var table = tables.getTable('event');
table.insert(item, {
success: function() {
console.log('Inserted event: ', item);
done();
}
});
}

Categories