使用
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键盘输入监听实现原理<br />- 起点是`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的返回值。```javascriptfunction* 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.stdinconst output = process.stdoutlet line = ''// 3、把输入的字符串一个个输出function onkeypress(s){output.write(s)line += sswitch (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)}
