var readline = require("readline");
var iface = new readline.Interface({
input: process.stdin,
output: process.stdout,
prompt: ""
});
var i = 0;
var string = 'Loading';
var done = false;
// Once ctrl-c is hit, it sets done to true.
iface.once('SIGINT', () => { done = true; });
test();
function test() {
// Clear the line, move the cursor to the beginning and print loading again
readline.clearLine(process.stdout, 0);
readline.moveCursor(process.stdout, (-string.length - 4), 0);
iface.write(string);
// print i dots
for (var n = 0; n < i; n++) {
iface.write('.');
}
if (done) {
// Make sure there are 3 dots on the line
for (var n = 0; n < 3 - i; n++) {
iface.write('.');
}
// Show that program is done then close.
iface.write('done\n');
iface.close();
return;
} else {
i = i < 3 ? i + 1 : 0; // Increment i if i is less than 3, otherwise set i to 0
setTimeout(test, 1000);
}
}
Code Objective:
Make a loop animating the printing of three dots to indicate that the computer is responsive and the app is not hanging. Loading is completed when the user presses Ctrl-C
. While the current code isn't the optimal way of doing things, it outputs like so:
Loading (clear line) Loading. (clear line) Loading.. (clear line) Loading... (clear line) Loading (clear line)...
Issue:
This code works pretty well for the first minute or so then it will suddenly print out all of the output history all at once. However, the program will still continue the animation as expected. After a certain amount of time, it will print out the output history again.
Here is the output to one of those instances:
LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..
Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.
Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...Loading
Loading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...
LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..
Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.
Loading..Loading...LoadingLoading
Workaround:
The issue was fixed when iface.write()
is replaced with process.stdout.write()
. This may have to do with how iface.write
works. Unless you are doing something that requires iface.write()
, it's probably best that you write directly to the stream object (IE. process.stdout.write()
).
Can reproduce with the last Node.js 4, 6, 7, and 8 on Windows 7 x64 with cmd.exe.
The problem is that readline.clearLine()
and readline.moveCursor()
functions operate on a raw TTY stream sending VT100 escape sequences and thereadline.Interface
knows nothing about it, these changes only happen on the screen while iface.line
and iface.cursor
just keep accumulating. When the total count of the characters that you've written via iface.write()
becomes equal to or exceeds the count of columns in your terminal, the interface refreshes the line while creating a new blank one and all those characters become visible.
In this case using process.stdout.write()
instead of iface.write()
is a perfectly valid solution, I don't see anything in this snippet of code that requires readline.Interface
functionality. (And yes, you are not obliged to use readline.Interface
if you use some functions of the readline
module itself; to the contrary, I'd say things like readline.clearLine()
and readline.moveCursor()
should have better been in the tty
module.)
However, if the code snippet in this issue is purely synthetic while you encountered the issue in the situation when using readline.Interface
is necessary, I'd suggest you another workaround:
18,19c18
< readline.clearLine(process.stdout, 0);
< readline.moveCursor(process.stdout, (-string.length - 4), 0);
---
> iface.write(null, { ctrl: true, name: 'u' });
This will send the "Ctrl+U" keys to the readline interface, effectively clearing the current line properly. Feel free to turn this issue into a feature-request for a new public method of readline.Interface
(there is an undocumented iface.clearLine()
already, but it doesn't do exactly what you need).
This issue has been inactive for sufficiently long that it seems like perhaps it should be closed. Feel free to re-open (or leave a comment requesting that it be re-opened) if you disagree. I'm just tidying up and not acting on a super-strong opinion or anything like that.
Most helpful comment
The problem is that
readline.clearLine()
andreadline.moveCursor()
functions operate on a raw TTY stream sending VT100 escape sequences and thereadline.Interface
knows nothing about it, these changes only happen on the screen whileiface.line
andiface.cursor
just keep accumulating. When the total count of the characters that you've written viaiface.write()
becomes equal to or exceeds the count of columns in your terminal, the interface refreshes the line while creating a new blank one and all those characters become visible.In this case using
process.stdout.write()
instead ofiface.write()
is a perfectly valid solution, I don't see anything in this snippet of code that requiresreadline.Interface
functionality. (And yes, you are not obliged to usereadline.Interface
if you use some functions of thereadline
module itself; to the contrary, I'd say things likereadline.clearLine()
andreadline.moveCursor()
should have better been in thetty
module.)However, if the code snippet in this issue is purely synthetic while you encountered the issue in the situation when using
readline.Interface
is necessary, I'd suggest you another workaround:This will send the "Ctrl+U" keys to the readline interface, effectively clearing the current line properly. Feel free to turn this issue into a feature-request for a new public method of
readline.Interface
(there is an undocumentediface.clearLine()
already, but it doesn't do exactly what you need).