使用Q创建异步用户输入序列

时间:2013-10-06 14:39:17

标签: node.js prompt q

我正在玩弄Qpromptly,我试图按顺序询问用户输入一些内容。例如:

What is your name? Bob
What is your age? 40
Hello Bob (40)! 

(是的!这是一个简单的“Hello world!”程序。)

以下是我正在尝试的代码,直接来自Q的github项目页面:

Q.fcall(promptly.prompt, "What is your name? ")
.then(promptly.prompt, "What is your age? ")
.done(function(name, age) {            
  console.log("Hello " + name + " (" + age + ")");
});

});

但它没有按预期工作(也许我读错了?)。无论我尝试什么,似乎promptly.prompt正在并行地听击键,并且.done函数被立即调用,导致

/path/to/node_modules/promptly/index.js:80
         fn(null, data);
         ^
TypeError: undefined is not a function
     at /path/to/node_modules/promptly/index.js:80:9
     ...

一旦我点击 Enter 。知道为什么会这样做以及我如何能够完成我想要做的事情?

**编辑**

基本上,我的最终目标是创建一个可重用的函数,如下所示:

promptAll({ 
    'name': "What is your name? ", 
    'age': "What is your age? "
}).done(function(input) {
    console.log(input);  // ex: { name: "Bob", age: 40 }
});

**更新**

这是我的工作解决方案,我必须按照WiredPraine的建议使用nfcall

function multiPrompt(args) {
  function _next() {
    if (keys.length) {
      var key = keys.pop();
      Q.nfcall(promptly.prompt, args[key]).done(function(value) {
        result[key] = value;
        _next();
      });
    } else {
      def.resolve(result);
    }
  };
  var def = Q.defer();  
  var keys = _.keys(args).reverse();
  var result = {};

  _next();

  return def.promise;
};

注意:我使用的是Underscore,但使用标准对象迭代器可以实现相同的效果。)

2 个答案:

答案 0 :(得分:2)

以下是两种方法。

首先,您需要使用nfcall,以便Q使用NodeJS约定进行回调。

但是,由于函数不是promise,你需要稍微不同地处理链接和同步行为。

在第一个示例start1中,代码创建defer的实例并将其作为promise返回。当prompt函数返回时,它resolve是延迟对象实例并传递函数的value(理想情况下是提示)。它还应该处理“真实”代码中的错误等。

在这两个例子中,我添加了一个函数来获取promise解析的结果。它不作为参数传递给最后一个done实例。传递给done的函数将在第一个承诺解决后立即执行(在这种情况下prompt返回后)。

var promptly = require('promptly');
var Q = require('q');

// make a simple deferred/promise out of the prompt function
var prompter = function(text) {
    var deferred = Q.defer();

    promptly.prompt(text, function(err, value) {
       deferred.resolve(value);
    });

    return deferred.promise;
};

// this option just uses the promise option to prompt for name.
function start1() {
    prompter("What is your name?").then(function(name) {
        prompter("Your age?").then(function(age) {
            console.log("Hello " + name + " (" + age + ")");
        });
    });
}

// this one uses the nfcall funcitonality to directly call the 
// promptly.prompt function (and waits for a callback).
function start2() {
    Q.nfcall(promptly.prompt, "What is your name? ")
        .then(function(name) {
                Q.nfcall(promptly.prompt, "What is your age? ")
                    .done(function(age) {
                        console.log("Hello " + name + " (" + age + ")");
                    });
        });
}

//start1();

答案 1 :(得分:1)

我觉得这里的答案可以添加到任何寻求替代解决从节点获取命令行用户输入的一般问题的人。

首先,我个人觉得转向ES6 Promises API是有好处的。虽然Node中还没有本机可用,但有一个很好的polyfill:https://github.com/jakearchibald/es6-promise

其次,我开始喜欢另一种用户提示模块:https://github.com/flatiron/prompt

现在假设存在方法'addUserToDb','printUser'和'printError'反过来返回promises,下面的例子是可能的:

var prompt = require('node-prompt');
var Promise = require('es6-promise').Promise;

var promptUser = function(schema) {
  return new Promise(resolve, reject) {
    prompt.get(schema, function(err, result) {
      if (err) {
        reject(err);
      } else {
        resolve(result);
      }
    });
  };
};

promptUser(["name", "password"])
  .then(addUserToDb)
  .then(printUser)
  .catch(printError)

我现在已经使用这种方法编写了很多“脚本”,并且发现它非常适合使用并且易于维护/适应。