Node.js同步提示

10

我正在使用Node.js的prompt库,代码如下:

var fs = require('fs'),
    prompt = require('prompt'),
    toCreate = toCreate.toLowerCase(),
    stats = fs.lstatSync('./' + toCreate);

if(stats.isDirectory()){
    prompt.start();
    var property = {
        name: 'yesno',
        message: 'Directory esistente vuoi continuare lo stesso? (y/n)',
        validator: /y[es]*|n[o]?/,
        warning: 'Must respond yes or no',
        default: 'no'
    };
    prompt.get(property, function(err, result) {                
        if(result === 'no'){
            console.log('Annullato!');
            process.exit(0);
        }
    });
}
console.log("creating ", toCreate);
console.log('\nAll done, exiting'.green.inverse);
如果提示框弹出,似乎不会阻止代码执行,而是继续执行并显示控制台的最后两条消息,同时我还需要回答问题。有没有一种方法可以使其阻塞?
如果弹出提示框,似乎不会阻止代码的执行,执行会在继续进行中,同时还会显示控制台中的最后两条消息,但我仍需回答这个问题。是否有方法能够使其阻塞?
9个回答

11

使用Flatiron的提示库,不幸的是,没有办法使代码阻塞。然而,我可以建议使用我的sync-prompt库。正如名称所示,它允许您同步提示用户输入。

使用它,您只需发出函数调用,就可以获得用户的命令行输入:

var prompt = require('sync-prompt').prompt;

var name = prompt('What is your name? ');
// User enters "Mike".

console.log('Hello, ' + name + '!');
// -> Hello, Mike!

var hidden = true;
var password = prompt('Password: ', hidden);
// User enters a password, but nothing will be written to the screen.

如果你想的话,可以试一试。

请注意:不要在Web应用程序中使用此方法。它只应该用于命令行应用程序。

更新:根本不要使用这个库。坦白说,这是一个彻头彻尾的玩笑。


3
无法在Ubuntu 14.04上安装。node-gyp rebuild - Vadorequest
2
最好使用 prompt-sync,它不需要 C++ 绑定并且适用于所有系统。 - Marco Ottolini
1
更新:完全不要使用这个库。坦率地说,它是一个彻头彻尾的玩笑。 ❓那么使用 prompt-sync - ruffin
@ruffin 不开玩笑,给自己个面子,别用 sync-prompt,试试其他的方案。 - Sal Rahman

8

自 Node.js 8 版本起,你可以使用 async/await 进行以下操作:

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

function readLineAsync(message) {
  return new Promise((resolve, reject) => {
    rl.question(message, (answer) => {
      resolve(answer);
    });
  });
} 

// Leverages Node.js' awesome async/await functionality
async function demoSynchronousPrompt() {
  var promptInput = await readLineAsync("Give me some input >");
  console.log("Won't be executed until promptInput is received", promptInput);
  rl.close();
}

很棒的答案。谢谢。 - Pierre
1
自从Node.js 18版本以后,这变得更加容易了;只需使用require('node:readline/promises')来引入(或导入),然后你就可以直接使用await rl.question(...)(无需使用此处显示的readLineAsync包装函数)。 - Marc Stober

4

由于Node中的IO不会阻塞,因此你不会找到一种简单的方法使这样的操作变成同步。相反,你应该将代码移入回调函数中:

  ...

  prompt.get(property, function (err, result) {               
    if(result === 'no'){
        console.log('Annullato!');
        process.exit(0);
    }

    console.log("creating ", toCreate);
    console.log('\nAll done, exiting'.green.inverse);
  });

否则提取它并调用提取的函数:
  ...

  prompt.get(property, function (err, result) {               
    if(result === 'no'){
        console.log('Annullato!');
        process.exit(0);
    } else {
        doCreate();
    }
  });

  ...

function doCreate() {
    console.log("creating ", toCreate);
    console.log('\nAll done, exiting'.green.inverse);
}

如果这意味着很多问题,那怎么办? - Matteo Pagliazzi
2
Node.js 本质上是异步的。如果这不是您特定应用程序的良好范例,那么 Node 对于该特定用例可能并不是正确的技术选择。您真的必须愿意接受框架的异步性质(像流程控制库和函数生成器等工具可以帮助很多)。 - Michelle Tilley
我已经对代码进行了一些重构,非常喜欢Node.js在大多数情况下的非阻塞I/O,但现在我正在尝试构建一个小型命令行实用程序,并且在这部分脚本中许多(如果不是全部)操作都应该按顺序进行,而使用回调并不总是可行。你知道有哪些好的流程控制库或模式吗? - Matteo Pagliazzi
我处于类似的情况中,试图创建一个类似于Thor's actions的Node工具。我非常喜欢async库;serieswaterfall可能特别适用于这个特定任务。 - Michelle Tilley
在尝试过后,我不确定异步是否真的有所帮助。看看这个Gist,看看它是否有任何意义:https://gist.github.com/77ce3468303351c06f29 - Michelle Tilley
@MatteoPagliazzi,当我开始开发NodeJS应用程序时,我自己也遇到了逻辑流是同步的情况。但随着我开始使用像Socket.ioRequest这样的库,我习惯了异步范例。但无论如何,当我需要同步流程时,我会使用(而且仍然在使用)Iced CoffeeScript。但请注意,在多个函数中使用此类库并不像它们看起来那么简单。 - Gautham Badhrinathan

3

虽然这是一个老问题,但我刚刚找到了解决方案。 readline-sync 提供了一种在node脚本中同步收集用户输入的方法。

它非常容易使用,不需要任何依赖项(由于gyp问题,我无法使用sync-prompt)。

来自Github自述文件:

var readlineSync = require('readline-sync');

// Wait for user's response.
var userName = readlineSync.question('May I have your name? ');
console.log('Hi ' + userName + '!');

我与该项目没有任何关联,但它让我的一天变得美好,所以我必须分享。


3
这是一个无需依赖、同步且适用于Windows、Linux和OSX的编程工具:
// Synchronously prompt for input
function prompt(message)
{
    // Write message
    process.stdout.write(message);

    // Work out shell command to prompt for a string and echo it to stdout
    let cmd;
    let args;
    if (os.platform() == "win32")
    {
        cmd = 'cmd';
        args = [ '/V:ON', '/C', 'set /p response= && echo !response!' ];
    }
    else
    {
        cmd = 'bash';
        args = [ '-c', 'read response; echo "$response"' ];
    }

    // Pipe stdout back to self so we can read the echoed value
    let opts = { 
        stdio: [ 'inherit', 'pipe', 'inherit' ],
        shell: false,
    };

    // Run it
    return child_process.spawnSync(cmd, args, opts).stdout.toString().trim();
}

2
我看到了这个帖子,所有的解决方案要么:
  • 没有真正提供同步提示解决方案
  • 已经过时,不适用于新版本的node。
因此,我创建了一个名为syncprompt的工具。使用npm i --save syncprompt进行安装,然后只需添加以下内容即可:
var prompt = require('syncprompt');

例如,这将使您能够执行以下操作:
var name = prompt("Please enter your name? ");

它还支持提示输入密码:

var topSecretPassword = prompt("Please enter password: ", true);

我目前的工作环境中没有Python可用,这也失败了。 - Iwnnay

1

Vorpal.js 是我最近发布的一个库。它提供了类似于您所询问的交互式提示符的同步命令执行功能。以下代码将实现您所要求的功能:

var vorpal = require('vorpal')();

vorpal.command('do sync')
  .action(function (args) {
    return 'i have done sync';
  });

以上代码,当一秒钟过去后(只有在回调函数被调用后),提示框才会出现。


看起来不是很同步。 - Tomáš Zato
哇,我一定是脑抽了。我展示了一个异步的例子,而不是同步的。我进行了编辑,包括同步版本。 - dthree

1
const buffer = Buffer.alloc(1024);
require("fs").readSync(process.stdin.fd, buffer);
console.log(buffer.toString());

这个答案有点奇怪。由于某种原因,在Windows上,process.stdin.fd无法注册控制序列。 - Battledash2

0

你可以使用prompt-sync

const prompt = require('prompt-sync')()

const ans = prompt('How many more times? ') // get input from the user.

顺便说一句,如果提示消息包含换行符,prompt-sync会表现得很奇怪,因此如果您需要多行提示,请使用console.log()

const prompt = require('prompt-sync')()

console.log('How many more times?\n')
const ans = prompt('') // get input from the user.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接