简体   繁体   English

如何使用node js创建交互式控制台界面?

[英]How to create an interactive console interface using node js?

I am creating an console interface,in which the program ask some questions and user answers it through console,there are questions where the user must only enter limited no of inputs,i found some ways to get console input in node js but cant find a way to limit users inputs and ask questions one after another, after asking questions want to create a JSON object from it.我正在创建一个控制台界面,程序在其中提出一些问题,用户通过控制台回答,有些问题用户只能输入有限的输入,我找到了一些在node js中获取控制台输入的方法,但找不到限制用户输入并依次提问的方法,在提问之后想从中创建一个JSON对象。 for eg,i will ask questions like:例如,我会问这样的问题:

  • What is your name?你叫什么名字?
  • List 3 of your hobbies?列出您的 3 项爱好?
  • Enter a desired username?输入所需的用户名?

After asking this questions i will construct a json object like问完这个问题后,我将构造一个json对象,例如

    {"name":"codebean","hobbies":['Exploring','Coding','Trucking'],"username":'codebean'}

Difficulties that i face are:我面临的困难是:

  • How to ask questions one after other, ie, serially如何一个接一个地提问,即串行提问

  • Limit the user inputs to a particular count将用户输入限制为特定计数

  • Terminate the program after entering the final question's answer输入最后一个问题的答案后终止程序

I only have little experience with NodeJs and what i was able to establish was nothing but rubbish,and here is what i established我对NodeJs的经验很少,我能建立的不过是垃圾,这就是我建立的

 process.stdin.setEncoding('utf8'); var input = []; process.stdin.on('readable', function() { console.log('What is your name'); var name = process.stdin.read(); if ((name.== null) && (name.== '/n')) { input;push(name.split('/n')[0]); console.log(input)? } console;log('List 3 of your hobbies.'). var hobbies = process;stdin;read(). }). process,stdin.on('end'; function() { console;log(input); });

Having made CLI applications in NodeJS, I would recommend using a library like prompt to better organize your code.在 NodeJS 中制作 CLI 应用程序后,我建议使用类似prompt的库来更好地组织代码。 A library would keep it more readable than what you can do naively (in my opinion).图书馆会让它比你天真地做的事情更具可读性(在我看来)。

However, if you want some native alternative, you could use Node's EventEmitter object to make things look a bit more organized, opposed to handling it all inside of the stdin callback:然而,如果你想要一些原生的替代方案,你可以使用 Node 的EventEmitter对象让事情看起来更有条理,而不是在标准输入回调中处理它:

var EventEmitter = require('events');
var prompt = new EventEmitter();
var current = null;
var result = {};
process.stdin.resume();

process.stdin.on('data', function(data){
  prompt.emit(current, data.toString().trim());
});

prompt.on(':new', function(name, question){
  current = name;
  console.log(question);
  process.stdout.write('> ');
});

prompt.on(':end', function(){
  console.log('\n', result);
  process.stdin.pause();
});

prompt.emit(':new', 'name', 'What is your name?');

prompt.on('name', function(data){
  result.name = data;
  prompt.emit(':new', 'hobbies', 'What are your hobbies?');
});

prompt.on('hobbies', function(data){
  result.hobbies = data.split(/,\s?/);
  prompt.emit(':new', 'username', 'What is your username?');
});

prompt.on('username', function(data){
  result.username = data;
  prompt.emit(':end');
});

This code uses some sort of a state-tracking method (I wouldn't know if there is an actual term).此代码使用某种状态跟踪方法(我不知道是否有实际术语)。

Basically, there is a variable that keeps track of what you programming is looking for, in our case that is current .基本上,有一个变量可以跟踪您的编程正在寻找的内容,在我们的例子中是current This variable is also used to trigger our prompt EventEmitter whenever data is received.每当收到数据时,此变量还用于触发我们的prompt EventEmitter。

Inside events we can change the current variable to ask for something else (I made a shorthand :new event to do this), manipulate the data however we please, and also add it input our result variable.在事件内部,我们可以更改current变量以请求其他内容(我做了一个速记:new事件来执行此操作),随心所欲地操作数据,并将其添加到我们的result变量中。

If you want to "tag" your input (a little marker at the beginning), you can do that simply with stdin.write :如果你想“标记”你的输入(开头的一个小标记),你可以简单地使用stdin.write来做到这一点:

prompt.on(':new', function(){
  // ...
  process.stdin.write('> ');
});

Here is what that code looks like in action:下面是该代码的实际效果:

$ node ...
What is your name?
> Jamen Marzonie
What are your hobbies?
> programming, philosophy, petting cats
What is your username?
> jamen

 { name: 'Jamen Marzonie',
  hobbies: [ 'programming', 'philosophy', 'petting cats' ],
  username: 'jamen' }

I routinely use coroutines for this case, which yield questions and consume answers.对于这种情况,我经常使用协程,它会产生问题并消耗答案。 Co-routines are generators which consume values to yield the next result.协程是生成器,它使用值来产生下一个结果。 In our case they consume the user's responses, and yield prompts.在我们的例子中,他们使用用户的响应,并产生提示。

This makes for supremely readable and debuggable sequences of prompts and simple state management.这使得提示序列和简单的状态管理具有极高的可读性和可调试性。

Note the use of function* in the example code which runs in node 14. This syntax defines a coroutine in javascript.请注意在节点 14 中运行的示例代码中function*的使用。此语法在 javascript 中定义了一个协程。

The first two 'library' functions in the example code at the end expose node's native stdio capabilities so that you can write 'stateful' coroutines such as...最后示例代码中的前两个“库”函数公开了节点的本机 stdio 功能,以便您可以编写“有状态”协程,例如...

function* createSimpleSequence(print) {
  print("Welcome to the game");
  let playerName = "";
  while(!playerName){
    playerName = yield "What is your name? ";
  }
  print(`Hello, ${playerName}`);
  yield "Press enter to restart the game";
}

Note that there is no explicit state management, as the co-routine allows us to 'pick up where we left off' after yielding a question, and we can use while loops and other apparently synchronous control flow primitives to decide how and when to 'progress' between states.请注意,没有明确的状态管理,因为协程允许我们在提出问题后“从中断的地方继续”,并且我们可以使用 while 循环和其他明显同步的控制流原语来决定如何以及何时“状态之间的进展。

The full example requested by the original poster would look like...原始海报要求的完整示例看起来像......

function* createOriginalPostersSequence(print) {
  let name = "";
  while (!name) {
    name = yield "What is your name?";
    if (!name) {
      print("Your name cannot be empty");
    }
  }

  let hobbyString = "";
  while (!hobbyString) {
    hobbyString = yield "List three of your hobbies, separated by ','";
    const hobbyCount = hobbyString.split(",").length;
    if (hobbyCount !== 3) {
      if (hobbyCount === 0) {
        print("Your hobbies cannot be empty");
      } else if (hobbyCount == 1) {
        print("What! Do you really only have one hobby, think again!");
      } else if (hobbyCount == 2) {
        print("Two is better than one, but I asked for three!");
      }
      hobbyString = "";
    }
  }
  const hobbies = hobbyString.split(",").map((hobby) => hobby.trim());

  let username = "";
  while (!username) {
    username = yield "What is your username?";
    if (!username) {
      print("Your username cannot be empty!");
    }
    if (!username.match(/[a-z_]+/)) {
      print(
        "Your username can only contain lowercase letters and underscores."
      );
      username = "";
    }
  }
  const data = {
    name,
    hobbies,
    username,
  };
  print(`Your full data is ${JSON.stringify(data)}`);
}

Finally this is the full source code which would run both the simple sequence, and then the original poster's requested interactive prompt sequence interactively in Node 14.最后,这是完整的源代码,它将运行简单序列,然后在节点 14 中以交互方式运行原始发布者请求的交互式提示序列。

// core 'library' exposing native node console capabilities for co-routines

function getAnswer() {
  process.stdin.resume();
  return new Promise((resolve) => {
    process.stdin.once("data", function (data) {
      resolve(data.toString().trim());
    });
  });
}

async function runSequence(sequenceFactory, clearScreen = true) {
  function print(msg, end = "\n") {
    process.stdout.write(msg + end);
  }
  let answer = undefined;
  const sequence = sequenceFactory(print);
  while (true) {
    const { value: question } = sequence.next(answer);
    if (question) {
      print(question, " : ");
      answer = await getAnswer();
      if (clearScreen) {
        console.clear();
      }
    } else {
      break;
    }
  }
}

// examples using the library

function* createSimpleSequence(print) {
  print("Welcome to the game");
  let playerName = "";
  while (!playerName) {
    playerName = yield "What is your name? ";
  }
  print(`Hello, ${playerName}`);
  yield "Press enter to restart the game";
}

function* createOriginalPostersSequence(print) {
  let name = "";
  while (!name) {
    name = yield "What is your name?";
    if (!name) {
      print("Your name cannot be empty");
    }
  }

  let hobbyString = "";
  while (!hobbyString) {
    hobbyString = yield "List three of your hobbies, separated by ','";
    const hobbyCount = hobbyString.split(",").length;
    if (hobbyCount !== 3) {
      if (hobbyCount === 0) {
        print("Your hobbies cannot be empty");
      } else if (hobbyCount == 1) {
        print("What! Do you really only have one hobby, think again!");
      } else if (hobbyCount == 2) {
        print("Two is better than one, but I asked for three!");
      }
      hobbyString = "";
    }
  }
  const hobbies = hobbyString.split(",").map((hobby) => hobby.trim());

  let username = "";
  while (!username) {
    username = yield "What is your username?";
    if (!username) {
      print("Your username cannot be empty!");
    }
    if (!username.match(/[a-z_]+/)) {
      print(
        "Your username can only contain lowercase letters and underscores."
      );
      username = "";
    }
  }
  const data = {
    name,
    hobbies,
    username,
  };
  print(`Your full data is ${JSON.stringify(data)}`);
}

// demo to run examples

async function run() {
  await runSequence(createSimpleSequence);
  await runSequence(createOriginalPostersSequence);
  process.exit(0);
}

run();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM