How to abort a readline interface question? - javascript

TL;DR
Once you call rl.question(query[, options], callback) it looks like there is no way to cancel the question as long als it's pending an answer.
Is there a way to cleanly abort a readline interface question?
I ran into a problem using the native Node.js Readline module:
I wanted to provide a simple pause-or-abort feature to intervene in a subroutine (if it takes too long or needs pausing while inspection). I achieved this by asking a question over stdio that can be answered parallel to the running subroutine. If an answer is given an intervention is started.
That works all fine. But if the subroutine finishes and no answer was given in that time, the question is no longer needed, so I'm looking for a clean way to "abort" the asked question.
Once you call rl.question(query[, options], callback) it looks like there is no way to cancel the question as long als it's pending an answer.
I created this test code to reproduce the problem:
// setting up the CLI
const rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
// ...somewhere later:
// mockup of subroutine starting
console.log('Start task...');
// ask question to intervene
rl.question('(p)ause/(a)bort: ', answer => {
console.log(`You entered ${answer}`);
// ...handle intervene.
});
// mockup of subroutine ending
setTimeout(()=>{
console.log('... task has ended. Question can be canelled.');
// ...cancel the question
}, 5000);
The temporary solution I came up with was to close the interface, clear the line and open a new interface:
let rl = // ...
//...
// mockup of subroutine ending
setTimeout(() => {
// ... cancel the question
rl.close();
process.stdout.clearLine();
process.stdout.cursorTo(0);
rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
console.log('... task has ended (and question was closed in a very dirty way).');
}, 5000);
It works... but this solution violates multiple coding conventions (separation of concerns, modularity...).
The original readline interface was initialized at a very different location of the program and I feel very unpleasant by casually closing and reopening it just like that. (Imagine another part of the code still keeps hold of the old instance, the original options for .createInterface() getting updated, etc.)
Is there a way to cleanly abort a readline interface question?

It sounds like you need an abort controller
// setting up the CLI
const rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
// mockup of subroutine starting
console.log('Start task...');
// ask question to intervene
const aborter = new AbortController();
rl.question('(p)ause/(a)bort: ', { signal: aborter.signal }, answer => {
console.log(`You entered ${answer}`);
// ...handle intervene.
});
// mockup of subroutine ending
setTimeout(() => {
console.log('... task has ended. Question can be caneled.');
aborter.abort();
}, 5000);
If your version of node doesn't support the abort controller, you can write to the interface directly
// setting up the CLI
const rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
// mockup of subroutine starting
console.log('Start task...');
// ask question to intervene
rl.question('(p)ause/(a)bort: ', answer => {
console.log(`You entered ${answer}`);
// ...handle 'e' answer as ended here here
// ...handle intervene.
});
// mockup of subroutine ending
setTimeout(() => {
console.log('... task has ended. Question can be caneled.');
rl.write("e\n");
}, 5000);

Related

How do I regularly update the UI while performing an asynchronic task?

Let's say I'm reading a very large file like so (or perform another task that takes a long time and continuously updates an object):
let myParsedObject = { };
const loadFile = async (filepath, filename) => {
const rl = readline.createInterface({
input: fs.createReadStream(path.join(filepath, filename)),
crlfDelay: Infinity
});
rl.on('line', (line) => { parseLineToObject(line) });
}
Is there a way to update the UI (electron in my case, but the same should apply to HTML) at regular intervals (e.g. 500ms), so I can track the progress? I know there is setInterval, but I can't figure out how to make it work.
In C++ I have to worry about the object being updated while writing to it, how should I deal with it in Javascript?
All I can think of is calling the update function every x operations, but that seems very arbitrary and can vary quite a lot.
Thank you in advance!

JavaScript I/O using cmd [duplicate]

This question already has answers here:
How to get input from user nodejs [duplicate]
(2 answers)
Closed 2 years ago.
I want to take input in javascript program using cmd, as I am executing the js program using node through cmd, So how can I do it?
Short Answer: Use readline (built-in nodejs module)
Long Answer:
According to this blog
Streams are the Node.js way of dealing with evented I/O - it's a big topic, and you can read more about them here. For now, we're going to use the built-in readline module which is a wrapper around Standard I/O, suitable for taking user input from command line(terminal).
Here's a simple example. Try the following in a new file:
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("What is your name ? ", function(name) {
rl.question("Where do you live ? ", function(country) {
console.log(`${name}, is a citizen of ${country}`);
rl.close();
});
});
rl.on("close", function() {
console.log("\nBYE BYE !!!");
process.exit(0);
});

nodejs how to wait all code done and get return value if too many async in the file

I want Nodejs to save the context like this :
It's just like run Nodejs in CMD or Powershell.
First of all, I type var temp = 10000; and maybe after 10 minutes I type var temp1 = temp it can still save the value of temp.
but in webstorm or some other IDE how can I do it?
for example, I have an abc.js and global.value =10 when I run abc.js it will close when the code is executed. I know some API like express. but if too many async functions in the file, it can't work well.
You can use the readline module to wait for user input and prevent the event loop from being done with. The below code prompts fo a user input, repeats what the user types or exit if the user types "quit". I hope the logic is pretty obvious.
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'Hello> '
});
rl.prompt();
rl.on('line', (line) => {
if (line === 'quit')
rl.close()
else {
console.log(line);
rl.prompt();
}
})

How to figure out what's holding Node.js from exiting?

I have this problem very often with various Node.js scripts that I write. After everything is done, they do not exit. Usually, it's an unclosed socket or readline interface.
When the script gets bigger, this is really hard to find out. Is there a tool of some sort that would tell me what's NodeJS waiting for? I'm asking for a generic solution that would help debug all cases of NodeJS not exiting when it's supposed to.
Samples:
Exhibit I. - Process stdin blocks node even after listener is removed
const readline = require('readline');
readline.emitKeypressEvents(process.stdin);
if (typeof process.stdin.setRawMode == "function")
process.stdin.setRawMode(true);
const keypressListener = (stream, key) => {
console.log(key);
process.stdin.removeListener("keypress", keypressListener);
}
process.stdout.write("Press any key...");
process.stdin.on("keypress", keypressListener);
Exhibit II. - readline blocks Node if you forget to close the interface
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
Exhibit III. - Forgotten setInterval will also block node, good luck finding it
setInterval(() => { }, 2000);
Would why-is-node-running work? Seems to do exactly what you need.

read file stream line by line synchronously

I'm looking at nodejs readline module documentation for a task where I've to read a very large file line by line and it looks good. But for my particular task, I need it to read lines synchronously ie. no matter what, line 5 must not be read before line 4, and due to nature of node, I just want to confirm that is this code safe for that usage -
const readline = require('readline');
const fs = require('fs');
const rl = readline.createInterface({
input: fs.createReadStream('sample.txt')
});
rl.on('line', (line) => {
console.log(`Line from file: ${line}`);
});
If not, what should I use/do? Currently it is working for me but I don't know if it'll work with large lines where next line could be parsed faster than previous one etc..
I doubt very much that it is possible, that the callback fired later can be executed earlier than another one.
Basically, it refers to the event loop and stack of the process.
Still, to guarantee I can suggest to implement something similar to async/queue, but with ability to dynamically push callbacks.
Assuming you will have something like this:
const Queue = require('./my-queue')
const queue = new Queue()
function addLineToQueue(line) {
queue.push(function() {
// do some job with line
console.log(`Line: "${line}" was successfully processed!`)
})
}
You will modify your code:
rl.on('line', (line) => {
addLineToQueue(line)
console.log(`Added line to queue: ${line}`)
})
And sure your queue implementation should start as far as it has any tasks to execute. This way the order of callbacks will be guaranteed. But as for me it looks like a little overhead.

Categories

Resources