使用

readline是Node.js中的一个内置库,主要是用来管理输入流的

  1. const readline = require('readline')
  2. const rl = readline.createInterface({
  3. input:process.stdin,
  4. output:process.stdout
  5. })
  6. rl.question('your name:',(answer =>{
  7. console.log('your name is:'+answer)
  8. rl.close()
  9. }))

断点进入第三行,会定义readline,给输入流stdin部署socket接口。
再次会进入createInterface的定义,

源码分析

  • 强制将函数转为构建函数

    1. function Interface(input, output, completer, terminal) {
    2. if (!(this instanceof Interface)) {
    3. return new Interface(input, output, completer, terminal);
    4. }
    5. …………
    6. }
  • 获得事件驱动能力

让this继承EventEmitter,让Interface实例,也就是上面的rl具备事件驱动能力。

  1. EventEmitter.call(this);
  • 监听键盘事件: ```javascript emitKeypressEvents(input, this);

// input usually refers to stdin input.on(‘keypress’, onkeypress); input.on(‘end’, ontermend);

  1. <a name="lUkKn"></a>
  2. ## 深入讲解readline键盘输入监听实现原理
  3. ![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)
  4. - 起点是`emitKeypressEvents`, `emitKey`是一个`generator`函数,调用`next`会执行到下一个yeid语句执行`input.on('newListener')`,然后继续执行`input.on('keypress')`监听键盘输入。
  5. - 监听键盘事件的事件回调里面函数`onNewListener`里面,是对用户输入流的监听`input.on('data')`,然后把事件监听`newListener`移除掉,并对用户输入进行逐字监听`input.setRowMode(true)`(默认是逐行监听)。
  6. - `input.resume`:把输入流由终止状态回复到输入状态,等待用户输入。
  7. - 当用户输入时,触发`input.on('data')`,会回调到`emitKeys`,通过`next`方法继续往下走,广播到`input.on('keypress')`的回调,判断用户是否输入了回车,是的话就结束。
  8. <a name="QkX10"></a>
  9. # generator的理解
  10. yield 命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。<br />一般情况下,next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。当 next 传入参数的时候,该参数会作为上一步yield的返回值。
  11. ```javascript
  12. function* sendParameter(){
  13. console.log("start");
  14. var x = yield '2';
  15. console.log("one:" + x);
  16. var y = yield '3';
  17. console.log("two:" + y);
  18. console.log("total:" + (x + y));
  19. }
  1. var sendp1 = sendParameter();
  2. sendp1.next();
  3. // start
  4. // {value: "2", done: false}
  5. sendp1.next();
  6. // one:undefined
  7. // {value: "3", done: false}
  8. sendp1.next();
  9. // two:undefined
  10. // total:NaN
  11. // {value: undefined, done: true}
  12. next传参
  13. var sendp2 = sendParameter();
  14. sendp2.next(10);
  15. // start
  16. // {value: "2", done: false}
  17. sendp2.next(20);
  18. // one:20
  19. // {value: "3", done: false}
  20. sendp2.next(30);
  21. // two:30
  22. // total:50
  23. // {value: undefined, done: true}

简易Readline代码实现

要点:这里用generator和事件留监听函数stream.on('data',onData)结合实现了事件循环。每次循环都emitkeypress事件,然后对输入的字符串进行处理。

  1. function stepRead (callback) {
  2. const input = process.stdin
  3. const output = process.stdout
  4. let line = ''
  5. // 3、把输入的字符串一个个输出
  6. function onkeypress(s){
  7. output.write(s)
  8. line += s
  9. switch (s){
  10. case '\r':
  11. // 把输入流终端,并把
  12. input.pause()
  13. callback(line)
  14. break
  15. }
  16. }
  17. emitKeypressEvents(input)
  18. input.on('keypress',onkeypress)
  19. input.on('end', ontermend);
  20. // 逐字监听
  21. input.setRawMode(true)
  22. input.resume()
  23. }
  24. function emitKeypressEvents(stream){
  25. const g = emitKey(stream)
  26. const onData = function(chunk){
  27. //当 next 传入参数的时候,该参数会作为上一步yield的返回值。
  28. g.next(chunk.toString())
  29. }
  30. // 1、先执行第一次,在yield语句之前执行
  31. g.next()
  32. // 1、监听输入流,在输入流里面再执行一次
  33. stream.on('data',onData)
  34. }
  35. function* emitKey (stream) {
  36. // 这里是一个while循环,相当于有无限个yield语句
  37. while (true){
  38. let ch = yield
  39. // 2、触发了keypress的监听函数,把刚才中断前的值emit出去
  40. stream.emit('keypress',ch)
  41. }
  42. }
  43. function ontermend(){
  44. console.log('ontermend',ontermend)
  45. }
  46. stepRead(fn)
  47. function fn ( data){
  48. console.log('我是回调',data)
  49. }