如何同时从函数返回值和promise?

时间:2016-02-24 23:15:28

标签: javascript node.js promise q

我正在做这样的事情

var command1;
var command2;

var fn = function(param) {
  var deferred = Q.defer();
  var command = spawn(..., [
    ... passing different arguments based on param ...
  ]);
  ...
  command.stdout.on('data', function(data) {
    if (/... if process started successfully .../.test(data)) {
      deferred.resolve();
    }
  });
  ...
  if (param === 'command1') {
     command1 = command;
  } else {
     command2 = command;
  }
  return deferred.promise;
};

Q.all([
  fn('command1'),
  fn('command2')
]);

以后我打电话给command1.kill()command2.kill()。我考虑过将command传递给resolve,但之后可能永远不会被调用。我也可以将command传递给reject,这样如果出现问题我就可以在kill打电话,但这感觉很奇怪。

如何以惯用的方式向呼叫者返回command和承诺?没有fn中的条件部分。有什么可能性?

我还考虑过ES6的解构分配功能,但请考虑以下内容

  ...
  return [command, deferred.promise];
}

[command1, promiseCommand1] = fn('command1');
[command2, promiseCommand2] = fn('command2');

Q.all([
  promise1,
  promise2.then(Q.all([
    promiseCommand1,
    promiseCommand2    
  ])
]);

但是失败了(至少在我的特定情况下,命令应该等到promise2被解决),因为当我通过promiseCommand1和{{1}时,进程已经在路上了} promiseCommand2

不确定我是否使用了正确的解构赋值语法。

刚刚进入我的脑海

Q.all

还有其他方式吗?

更新

从昨天开始,我已经想出如何使用解构赋值(仍然不确定语法)

var command1;
var command2;

var fn = function(param, callback) {
  var deferred = Q.defer();
  var command = spawn(..., [...]);
  ...
  callback(command);
  return deferred.promise; 
};

Q.all([
  fn('command1', function(command) {
    command1 = command;
  }),
  fn('command1', function(command) {
    command2 = command;
  })
]);

这样命令只会在Q.all([ promise1, promise2.then(function() { [command1, promiseCommand1] = fn('command1'); [command2, promiseCommand2] = fn('command2'); return Q.all([ promiseCommand1, promiseCommand2 ]); }) ]); 解析后执行。

解决方案

基于the accepted answer和我之前的更新,我想出了这个

promise2

工作,对我来说似乎是一个简洁的解决方案。我不想依赖ES6进行解构赋值。另外,我认为我不能使用该功能将一个值分配给在作用域外声明的变量,并简明地在本地作用域中指定另一个。返回

  command.promise = deferred.promise;
  return command;
};

Q.all([
  promise1,
  promise2.then(function() {
    command1 = fn('command1');
    command2 = fn('command2');    
    return Q.all([command1.promise, command2.promise]);
  })
]);

也是一种可能的解决方案,但不够简洁。

return {
  command: command,
  promise: deferred.promise
};

校正

在接受的答案的评论部分,我被建议在Q.all([ promise1, promise2.then(function() { var result1 = fn('command1'); var result2 = fn('command2'); command1 = result1.command; command2 = result2.command; return Q.all([result1.promise, result2.promise]); }) ]); 中致电reject,以防止我的代码因未决承诺而永久挂起。我用以下

解决了这个问题
fn

使用 command.promise = deferred.promise.timeout(...); return command; }; 将返回相同的承诺,但是如果未在给定的超时值中解析承诺,则承诺将自动被拒绝。

2 个答案:

答案 0 :(得分:1)

您可以返回一个数组,然后使用promise.spread方法。

https://github.com/kriskowal/q#combination

.then(function () {
    return [command, promise];
})
.spread(function (command, promise) {
});

答案 1 :(得分:1)

你应该通过将“传递命令拒绝”放在头上来获得有用的东西。换句话说,拒绝响应kill()命令。

正如您所知,问题是fn()应该返回一个承诺,而承诺并不会自然地传达相应命令的.kill()方法。但是,javascript允许将属性(包括函数(作为方法))动态附加到对象。因此,添加.kill()方法很简单。

var fn = function(param) {
    var deferred = Q.defer();
    var command = spawn(..., [
        ... passing different arguments based on param ...
    ]);
    ...
    command.stdout.on('data', function(data) {
        if (/... if process started successfully .../.test(data)) {
            deferred.resolve();
        }
    });
    ...
    var promise = deferred.promise;
    // Now monkey-patch the promise with a .kill() method that fronts for command.kill() AND rejects the Deferred.
    promise.kill = function() {
        command.kill();
        deferred.reject(new Error('killed')); // for a more specific error message, augment 'killed' with something unique derived from `param`. 
    }
    return promise;
};

var promise1 = fn('command1');
var promise2 = fn('command2');

Q.all([promise1, promise2]).spread(...).catch(...);

promise1.kill()promise2.kill()会导致“已杀死”在捕获处理程序中显示为error.message

可以酌情调用两个杀戮,例如......

if(...) {
    promise1.kill();
}
if(...) {
    promise2.kill();
}

或者,.kill()方法也可以干净地分离,而不需要.bind(),例如:

doSomethingAsync(...).then(...).catch(promise1.kill);
doSomethingElseAsync(...).then(...).catch(promise2.kill);

请注意,fn()适用于任意数量的来电,无需外部变量command1command2等。