Node.js readline交互输出异常及终端行清除优化问询
终端readline输入后重写问题与输入的解决方案
问题描述
需要实现:用户按下回车键后,将readline问题的用户输入显示在问题前方。现有一个promisify封装的question函数,尝试通过process.stdout.write清除终端行并重新打印内容,但第二个process.stdout.write无效果;使用console.log可正常工作但不想使用该方法,同时希望找到比手动编写ANSI转义序列更可靠的清除终端最后两行的方式。
原始代码:
export const promisifyQuestion = question => { return new Promise(resolve => rl.question(chalk.cyan.bold(`${question}\n`), res => { resolve(res); }), ); };
修改后无效的代码:
export const promisifyQuestion = question => { return new Promise(resolve => rl.question(chalk.cyan.bold(`${question}\n`), res => { process.stdout.write("\u001B[2k\u001B[2A\u001B[G"); process.stdout.write(`${chalk.cyan.bold(question)} ${chalk.yellow(res)}`); resolve(res); }), ); };
可复现示例:
const { createInterface } = require("readline") const rl = createInterface({ input: process.stdin, output: process.stdout, }); const promisifyQuestion = question => { return new Promise(resolve => rl.question(`${question}\n`, res => { process.stdout.write("\u001B[2k\u001B[2A\u001B[G"); //console.log(`${question} ${res}`); process.stdout.write(`${question} ${res}`); resolve(res); }), ); }; rl.on("close", () => { process.exit(0); }); async function test() { await promisifyQuestion("What is your name?"); } test()
解决方案
1. 修复process.stdout.write无效问题
问题根源是readline会自行管理终端光标和输出,直接调用process.stdout.write会和readline的控制逻辑冲突。可以通过readline提供的光标控制方法精准操作终端内容:
修改后的promisifyQuestion函数(带chalk版本):
export const promisifyQuestion = question => { return new Promise(resolve => rl.question(chalk.cyan.bold(`${question}\n`), res => { // 光标上移2行,回到问题所在行 rl.moveCursor(process.stdout, 0, -2); // 清除当前整行(原问题行) rl.clearLine(process.stdout, 0); // 将光标移到当前行开头 rl.cursorTo(process.stdout, 0); // 写入带输入内容的问题并换行 process.stdout.write(`${chalk.cyan.bold(question)} ${chalk.yellow(res)}\n`); resolve(res); }), ); };
不带chalk的可复现代码修改版:
const { createInterface } = require("readline") const rl = createInterface({ input: process.stdin, output: process.stdout, }); const promisifyQuestion = question => { return new Promise(resolve => rl.question(`${question}\n`, res => { rl.moveCursor(process.stdout, 0, -2); rl.clearLine(process.stdout, 0); rl.cursorTo(process.stdout, 0); process.stdout.write(`${question} ${res}\n`); resolve(res); }), ); }; rl.on("close", () => { process.exit(0); }); async function test() { await promisifyQuestion("What is your name?"); } test()
2. 更可靠的终端行清除方法
避免手动编写ANSI转义序列,使用Node.js readline模块提供的官方跨平台方法:
rl.moveCursor(stream, dx, dy):移动光标到指定位置rl.clearLine(stream, dir):清除当前行内容,dir参数说明:- 0:清除整行
- 1:清除光标到行尾
- -1:清除行首到光标
rl.clearScreenDown(stream):清除从光标到屏幕底部的所有内容
如果需要清除最后两行,可按以下逻辑实现:
// 清除最后两行 rl.moveCursor(process.stdout, 0, -2); // 上移2行到第一行待清除行 rl.clearLine(process.stdout, 0); // 清除第一行 rl.moveCursor(process.stdout, 0, 1); // 下移1行到第二行待清除行 rl.clearLine(process.stdout, 0); // 清除第二行 rl.moveCursor(process.stdout, 0, -1); // 回到第一行开头
内容的提问来源于stack exchange,提问作者Leandro Melo
相关产品推荐
相关产品推荐

