我正在创建一个控制台界面,其中程序提出一些问题,用户通过控制台回答它,有些问题用户必须只输入有限的输入,我找到了一些方法来获取控制台输入{{1但在找到想要从中创建node js
对象的问题之后,无法找到一种限制用户输入并逐个问问题的方法。
例如,我会问类似的问题:
在提出这些问题后,我将构建一个JSON
对象,如
json
我面临的困难是:
如何一个接一个地提问,即连续提问
将用户输入限制为特定计数
输入最终问题的答案后终止程序
我对 {"name":"codebean","hobbies":['Exploring','Coding','Trucking'],"username":'codebean'}
只有很少的经验,而我能够建立的只不过是垃圾,这就是我所建立的
NodeJs
答案 0 :(得分:10)
在NodeJS中创建了CLI应用程序后,我建议使用像prompt
这样的库来更好地组织代码。一个图书馆可以让它比你天真可以做的更具可读性(在我看来)。
但是,如果你想要一些本地替代方案,你可以使用Node的EventEmitter
object来使事情看起来更有条理,而不是在stdin回调中处理它:
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');
});
此代码使用某种状态跟踪方法(我不知道是否有实际术语)。
基本上,有一个变量可以跟踪你编程的内容,我的情况是current
。此变量还用于在收到数据时触发prompt
EventEmitter。
在内部事件中,我们可以更改current
变量以请求其他内容(我做了一个速记:new
事件来执行此操作),然后操纵数据,我们也请输入我们的{ {1}}变量。
如果您想“标记”您的输入(开头的一个小标记),您只需使用result
即可:
stdin.write
以下是该代码的实际效果:
prompt.on(':new', function(){
// ...
process.stdin.write('> ');
});
答案 1 :(得分:0)
我经常在这种情况下使用协程,它会产生问题并消耗答案。协程是生成器,它消耗值来产生下一个结果。在我们的例子中,它们使用用户的响应,并产生提示。
这使得提示序列和简单的状态管理具有极高的可读性和可调试性。
请注意在节点 14 中运行的示例代码中使用了 function*
。此语法在 javascript 中定义了一个协程。
末尾示例代码中的前两个“库”函数公开了节点的原生 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";
}
请注意,没有明确的状态管理,因为协同例程允许我们在产生问题后“从我们离开的地方开始”,并且我们可以使用 while 循环和其他明显同步的控制流原语来决定如何以及何时在状态之间“进步”。
原始海报要求的完整示例看起来像...
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)}`);
}
最后,这是完整的源代码,它将在 Node 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.stdin.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();