Reputation: 10151
I'm doing something like this
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')
]);
and later on I'm calling command1.kill()
and command2.kill()
. I thought about passing command
to resolve
, but then it may never be called. I could pass command
to reject
also, so that I could call kill
there if something goes wrong, but that feels weird.
How do I return command
and the promise as well to the caller in an idiomatic way? Without the conditional part in fn
. What are the possibilities?
I also thought about ES6's deconstructing assignment feature, but consider the following
...
return [command, deferred.promise];
}
[command1, promiseCommand1] = fn('command1');
[command2, promiseCommand2] = fn('command2');
Q.all([
promise1,
promise2.then(Q.all([
promiseCommand1,
promiseCommand2
])
]);
but this fails (at least in my particular case, where the commands should wait until promise2
is resolved), because the processes are already on their way when I pass promiseCommand1
and promiseCommand2
to Q.all
.
Not sure if I used the correct deconstructing assignment syntax.
Just popped into my mind
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;
})
]);
Any other way?
Since yesterday I've figured out how could I might use a deconstructing assignment (still not sure on the syntax)
Q.all([
promise1,
promise2.then(function() {
[command1, promiseCommand1] = fn('command1');
[command2, promiseCommand2] = fn('command2');
return Q.all([
promiseCommand1,
promiseCommand2
]);
})
]);
this way the commands will only be executed after promise2
is resolved.
Based on the accepted answer and my previous update I came up with this
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]);
})
]);
Works and seems to be a concise solution for me. I don't want to rely on ES6 for the deconstructing assignment. Also, I don't think I can use the feature to assign one value to a variable declared outside of the scope and assign the other in the local scope concisely. Returning
return {
command: command,
promise: deferred.promise
};
is also a possible solution, but less concise.
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]);
})
]);
In the comment section in the accepted answer I was advised to call reject
in fn
to prevent my code hang forever because of a pending promise. I've solved this with the following
command.promise = deferred.promise.timeout(...);
return command;
};
Using timeout
will return the same promise, however if the promise is not resolved in given timeout value the promise will be rejected automatically.
Upvotes: 0
Views: 384
Reputation: 19288
You should end up with something useful by turning your "pass command to reject" on its head. In other words, reject in response to a kill()
command.
The trouble, as you know, is that fn()
should return a promise, and a promise doesn't naturally convey the corresponding command's .kill()
method. However javascript allows the dynamic attachment of properties, including functions (as methods), to objects. Therefore adding a .kill()
method is simple.
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()
or promise2.kill()
will give rise to "killed" appearing as error.message
in the catch handler.
The two kills can be called as appropriate, for example ...
if(...) {
promise1.kill();
}
if(...) {
promise2.kill();
}
Alternatively, the .kill()
methods will also detach cleanly without needing to .bind()
, for example :
doSomethingAsync(...).then(...).catch(promise1.kill);
doSomethingElseAsync(...).then(...).catch(promise2.kill);
Note that fn()
will work for any number of calls without the need for outer vars command1
, command2
etc.
Upvotes: 1
Reputation: 1434
You can return an array and then use promise.spread
method.
https://github.com/kriskowal/q#combination
.then(function () {
return [command, promise];
})
.spread(function (command, promise) {
});
Upvotes: 1