另一个答案很好,但不必要地使用了递归。
解决这个问题的关键是在您的脑海中将其他语言中使用的基于循环的简单方法与 Node 的异步方法分开。
在其他语言中,您可能会使用这样的循环:
while not finished:
line = readline.read()
if line == 'woof':
print('BARK')
elif line == 'exit':
finished = True
... # etc
Node,至少对于 Readline,不能以这种方式工作。
在 Node 中,您启动 Readline,为其提供事件处理程序,然后返回,然后处理 readline 循环的完成稍后。
考虑一下这段代码,你可以复制粘贴运行:
const readline = require('readline');
function replDemo() {
return new Promise(function(resolve, reject) {
let rl = readline.createInterface(process.stdin, process.stdout)
rl.setPrompt('ready> ')
rl.prompt();
rl.on('line', function(line) {
if (line === "exit" || line === "quit" || line == 'q') {
rl.close()
return // bail here, so rl.prompt() isn't called again
}
if (line === "help" || line === '?') {
console.log(`commands:\n woof\n exit|quit\n`)
} else if (line === "woof") {
console.log('BARK!')
} else if (line === "hello") {
console.log('Hi there')
} else {
console.log(`unknown command: "${line}"`)
}
rl.prompt()
}).on('close',function(){
console.log('bye')
resolve(42) // this is the final result of the function
});
})
}
async function run() {
try {
let replResult = await replDemo()
console.log('repl result:', replResult)
} catch(e) {
console.log('failed:', e)
}
}
run()
运行这个,你会得到这样的输出:
$ node src/repl-demo.js
ready> hello
Hi there
ready> boo
unknown command: "boo"
ready> woof
BARK!
ready> exit
bye
repl result: 42
注意run 函数调用replDemo 并“等待”承诺的结果。
如果你对 async/await 不熟悉,下面是同样的逻辑,写成“传统”的 Promise 风格:
function run2() {
replDemo().then(result => {
console.log('repl result:', result)
}).catch(e => {
console.log('failed:', e)
})
console.log('replDemo has been called')
}
请注意,我添加输出“replDemo 已被调用”是有原因的 - 运行上面的代码会显示如下输出:
$ node src/repl-demo.js
ready> replDemo has been called
woof
BARK!
ready> hello
Hi there
ready> bye
repl result: 42
注意“replDemo 已被调用”是如何在第一个“ready>”提示之后立即出现的。那是因为replDemo() 函数立即返回,然后run2() 立即退出,而main 全部完成——但readline 仍在执行!
如果您像我这样具有命令式编程背景,这很难掌握。 nodejs核心的异步事件驱动循环一直运行,直到所有工作完成,这发生在最后一个promise被解决时,这发生在readline实例“关闭”时,当输入“exit”时发生用户(或收到 EOF,在大多数系统上是 CTRL+D,在 Windows 上是 CTRL+Z)。