使用
readline是Node.js中的一个内置库,主要是用来管理输入流的
const readline = require('readline')
const rl = readline.createInterface({
input:process.stdin,
output:process.stdout
})
rl.question('your name:',(answer =>{
console.log('your name is:'+answer)
rl.close()
}))
断点进入第三行,会定义readline
,给输入流stdin
部署socket
接口。
再次会进入createInterface
的定义,
源码分析
强制将函数转为构建函数
function Interface(input, output, completer, terminal) {
if (!(this instanceof Interface)) {
return new Interface(input, output, completer, terminal);
}
…………
}
获得事件驱动能力
让this继承EventEmitter
,让Interface
实例,也就是上面的rl
具备事件驱动能力。
EventEmitter.call(this);
- 监听键盘事件: ```javascript emitKeypressEvents(input, this);
// input
usually refers to stdin
input.on(‘keypress’, onkeypress);
input.on(‘end’, ontermend);
<a name="lUkKn"></a>
## 深入讲解readline键盘输入监听实现原理
![image.png](https://cdn.nlark.com/yuque/0/2021/png/243804/1624608864430-6363c40a-bc2a-4b7b-9124-11a5c3aaf351.png#clientId=u4fd533f6-6d65-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u5ed192e8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=294&originWidth=746&originalType=url&ratio=1&rotation=0&showTitle=false&size=53981&status=done&style=none&taskId=uea81aecc-277a-47e2-a04d-65d68efdb4e&title=)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/282427/1662778650434-e5373c16-da34-4406-9eaa-fd44f940bc10.png#clientId=u092151dd-a80b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=332&id=u2f9285b4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=506&originWidth=883&originalType=binary&ratio=1&rotation=0&showTitle=false&size=128100&status=done&style=none&taskId=uf6254b0c-fb85-43ce-8eb4-237b6c5e5ea&title=&width=579.5)
- 起点是`emitKeypressEvents`, `emitKey`是一个`generator`函数,调用`next`会执行到下一个yeid语句执行`input.on('newListener')`,然后继续执行`input.on('keypress')`监听键盘输入。
- 监听键盘事件的事件回调里面函数`onNewListener`里面,是对用户输入流的监听`input.on('data')`,然后把事件监听`newListener`移除掉,并对用户输入进行逐字监听`input.setRowMode(true)`(默认是逐行监听)。
- `input.resume`:把输入流由终止状态回复到输入状态,等待用户输入。
- 当用户输入时,触发`input.on('data')`,会回调到`emitKeys`,通过`next`方法继续往下走,广播到`input.on('keypress')`的回调,判断用户是否输入了回车,是的话就结束。
<a name="QkX10"></a>
# generator的理解
yield 命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。<br />一般情况下,next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。当 next 传入参数的时候,该参数会作为上一步yield的返回值。
```javascript
function* sendParameter(){
console.log("start");
var x = yield '2';
console.log("one:" + x);
var y = yield '3';
console.log("two:" + y);
console.log("total:" + (x + y));
}
var sendp1 = sendParameter();
sendp1.next();
// start
// {value: "2", done: false}
sendp1.next();
// one:undefined
// {value: "3", done: false}
sendp1.next();
// two:undefined
// total:NaN
// {value: undefined, done: true}
next传参
var sendp2 = sendParameter();
sendp2.next(10);
// start
// {value: "2", done: false}
sendp2.next(20);
// one:20
// {value: "3", done: false}
sendp2.next(30);
// two:30
// total:50
// {value: undefined, done: true}
简易Readline代码实现
要点:这里用generator
和事件留监听函数stream.on('data',onData)
结合实现了事件循环。每次循环都emit
了keypress
事件,然后对输入的字符串进行处理。
function stepRead (callback) {
const input = process.stdin
const output = process.stdout
let line = ''
// 3、把输入的字符串一个个输出
function onkeypress(s){
output.write(s)
line += s
switch (s){
case '\r':
// 把输入流终端,并把
input.pause()
callback(line)
break
}
}
emitKeypressEvents(input)
input.on('keypress',onkeypress)
input.on('end', ontermend);
// 逐字监听
input.setRawMode(true)
input.resume()
}
function emitKeypressEvents(stream){
const g = emitKey(stream)
const onData = function(chunk){
//当 next 传入参数的时候,该参数会作为上一步yield的返回值。
g.next(chunk.toString())
}
// 1、先执行第一次,在yield语句之前执行
g.next()
// 1、监听输入流,在输入流里面再执行一次
stream.on('data',onData)
}
function* emitKey (stream) {
// 这里是一个while循环,相当于有无限个yield语句
while (true){
let ch = yield
// 2、触发了keypress的监听函数,把刚才中断前的值emit出去
stream.emit('keypress',ch)
}
}
function ontermend(){
console.log('ontermend',ontermend)
}
stepRead(fn)
function fn ( data){
console.log('我是回调',data)
}