const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question("your name: ", (answer) => {
console.log(answer);
});
process.stdin.write("your name");
- 点击回车键
return,\r
- 调用emitKeys,返回一个generator函数,但是没有执行函数里面的内容
- 调用next,执行到yield为止的代码
input.on('keypress', onkeypress)// events执行on方法(改写了on方法),判断input是否监听了newListener事件,如果存在则会emit newListener事件- 红线表示:用户输入的过程
- input.resume:如果input中断,则进行恢复
function createInterface(input, output, completer, terminal) {
return new Interface(input, output, completer, terminal);
}
function Interface(input, output, completer, terminal) {
if (!(this instanceof Interface)) { // 如果不是以构造函数调用的,则转化为构造函数调用
return new Interface(input, output, completer, terminal);
}
if (!this.terminal) {
...
} else {
emitKeypressEvents(input, this); //核心代码
input.on('keypress', onkeypress); // events执行on方法(改写了on方法),判断input是否监听了newListener事件,如果存在则会emit newListener事件,并把'keypress'事件名称作为参数带给方法
input.on('end', ontermend); // 执行onNewListener事件
this.line = '';
this._setRawMode(true); //默认是逐行监听,true为逐字监听
}
input.resume();
}
emitKeypressEvents方法
const KEYPRESS_DECODER = Symbol('keypress-decoder');
const ESCAPE_DECODER = Symbol(escape-decoder)
emitKeypressEvents(){
if (stream[KEYPRESS_DECODER]) return; // 单例,如果执行过函数,就不在执行
stream[KEYPRESS_DECODER] = new StringDecoder('utf8');
stream[ESCAPE_DECODER] = emitKeys(stream); //核心代码,返回一个generator函数,只有调用next方法才会执行函数内容,执行到yield位置;函数被中断
stream[ESCAPE_DECODER].next(); //执行emitKeys到yield位置,ch=undefined
const triggerEscape = () => stream[ESCAPE_DECODER].next(''); //键盘点击esc的时候
stream.on('newListener', onNewListener); //执行结束
}
function onNewListener(event) { // 在input.on('keypress',onkeypress) 和 stream.on('data', onData); 的时候;执行on方法都会被执行。
if (event === 'keypress') {
// 调用on会先执行onNewListener方法,第二次event='data'
// 关键代码,给输入流监听用户输入,就会处于等待状态。
stream.on('data', onData);
stream.removeListener('newListener', onNewListener); //event === 'keypress'的时候才会被执行
}
}
//此时有end,pause,data,keypress事件
- 输入流上定义了‘data’,‘keypress’事件
- 执行emitKey方法到第一个yield位置
用户输入
- 用户输入字符:进入data事件,触发onData方法
- 执行generator函数,写入字符
- 调用onKeypress方法
- 用户点击return,回车,进入data事件,触发onData方法
- 调用callback
function onData(input) {
if (stream.listenerCount('keypress') > 0) {
const string = stream[KEYPRESS_DECODER].write(input);
if (string) {
for (const character of string) {
length += character.length;
if (length === string.length) {
iface.isCompletionEnabled = true;
}
try {
stream[ESCAPE_DECODER].next(character); //关键代码
// Escape letter at the tail position
if (length === string.length && character === kEscape) {
timeoutId = setTimeout(triggerEscape, escapeCodeTimeout);
}
}
}
}
} else {
// Nobody's watching anyway
stream.removeListener('data', onData);
stream.on('newListener', onNewListener);
}
}
手写简易版readline
function stepStream(cb){
let input = process.stdin;
let output = process.stdout;
let line = '';
function onkeypress(s){
output.write(s);
line += s;
switch (s) {
case '\r':
input.pause();
cb(line);
break
}
}
emitKeypressEvents(input);
input.on('keypress',onkeypress)
input.setRawMode(true); //所有的字符都自定义,多会触发到onData方法
input.resume();
};
function emitKeypressEvents(stream){
function onData(chunk){
g.next(chunk.toString());
}
const g = emitKeys(stream);
g.next();
stream.on('data',onData)
}
function* emitKeys(stream){
while (true){
let ch = yield ;
stream.emit('keypress',ch)
}
}
stepStream(function(answer){
console.log('answer',answer);
})