I\'m using the following code which working OK, but the problem is that when I get an error, I want it to stops all the other promises. For example if chi.getCommand(val1,
Well, in your actual code (the one from UPDATE1) you are running getCommand concurrently to getStatus, not in sequence. You're calling (starting) both of them before the child process fails, and when it does there is nothing that would stop getStatus.
Just chain them together like in your first snippet, where a rejection in getCommand will cause getStatus to not run at all. You can use
childP.getCommand('spawn', cmd)
.timeout(5000)
.then(function(cmdresult) {
return app.getStatus(51000, 10, 1);
}).catch(function (err) {
console.log("An error occured: " + err);
});
As @Esailija pointed out bluebird has cancellation mechanism built-in - which is really nice and for sure totally fine for simple async computations.
Promise.config({
cancellation: true
});
function createCancellableMock(result, time) {
return new Promise(function(resolve, reject, onCancel) {
// var child = runCommand();
var token = setTimeout(function() {
if (result) {
console.log('almost done', result);
resolve(result);
} else {
reject('_ERR_');
}
}, time);
onCancel(function() {
console.log('cancelling');
// child.kill('SIGTERM');
clearTimeout(token);
})
})
}
var op1 = createCancellableMock('ok-1', 1000);
//var op2 = createCancellableMock('ok-2', 500);
var op2 = createCancellableMock(null, 500); // will be rejected
Promise.all([op1, op2])
.spread(function(v1, v2) {
console.log('BOTH-OK', v1, v2)
})
.catch(function() {
console.error('ERROR');
op1.cancel();
})
.finally(function() {
console.log('finally');
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.0/bluebird.core.js"></script>
You can cancel recursively defined actions (such as retries). The best strategy in such a case is not to mangle the action itself with the recursive behavior. In the below snippet I created a very simple wrapper which illustrates my point.
var TOO_MANY_RETRIES_ERROR = 'too_many_retries_error';
var PROB_OF_FAIL = 0.8;
var INTERVAL = 200;
var RETRIES = 5;
var CANCEL_AFTER = null;
//var CANCEL_AFTER = INTERVAL * (RETRIES/2);
Promise.config({
cancellation: true
});
function retryWithCancel(params) {
// params = {op - operation to retry (it should return a promise, which either ),
// interval - between retries, retries - number of retries }
console.log('running, retries left ', params.retries);
params = Object.assign({}, params); // copy params - no side-effects please
params.retries--;
if (params.retries <= 0) {
console.error('too many retries');
return Promise.reject(new Error(TOO_MANY_RETRIES_ERROR));
}
return new Promise(function(resolve, reject, onCancel) {
var o = params.op()
.catch(function() {
return Promise.delay(params.interval)
.then(retryWithCancel.bind(null, params))
.catch(reject)
})
.then(resolve)
onCancel(function() {
console.log('Cancelling, retries left: ', params.retries);
o.cancel();
});
})
}
function fakeOperation() {
return Promise.delay(100)
.then(function() {
if (Math.random() > PROB_OF_FAIL) {
return Promise.resolve('SUCCESS');
} else {
return Promise.reject(new Error('ERROR'));
}
})
}
var p = retryWithCancel({
op: fakeOperation,
interval: INTERVAL,
retries: RETRIES
})
.then(console.log.bind(console))
.catch(console.error.bind(console))
.finally(console.log.bind(console, 'done'))
if (CANCEL_AFTER) {
setTimeout(function() {
p.cancel();
}, CANCEL_AFTER)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.1/bluebird.js"></script>
In general promises are great but they do not offer cancellation mechanism out of the box. It is pretty problematic in some scenarios (e.g. https://github.com/whatwg/fetch/issues/27) and in your case option to cancel would be pretty handy as well. The only valid option is to add it yourself.
I distilled the problem to bare minimum and made it browser runnable.
The downside of the below approach is that after cancellation the promise will never resolve nor reject - which in general case is surely unacceptable. Alternatively .cancel may reject the promise with some special symbol. Neither of these approaches seem elegant.
function createCancellableMock(result, time) {
// child = null;
var token = null ;
var p = new Promise(function(resolve, reject) {
// child = runCommand();
token = setTimeout(function() {
if (result) {
console.log('almost done', result);
resolve(result);
}
else {
reject('_ERR_');
}
}, time);
}
)
return {
promise: p,
cancel: function() {
console.log('cancelling');
// child.kill('SIGTERM');
clearTimeout(token);
}
}
}
var op1 = createCancellableMock('ok-1', 1000);
// var op2 = createCancellableMock('ok-2', 500);
var op2 = createCancellableMock(null, 500); // will be rejected
Promise.all([op1.promise, op2.promise])
.then(function(vs) { // no spread in native implemantation
console.log('BOTH-OK', vs[0], vs[1])
})
.catch(function() {
console.error('ERROR');
op1.cancel();
})
For basic sequence of operations promises are fine, but there is a way more superior approach available: namely observables. Not only they offer built-in cancellation / disposing mechanism, but allow to deal with multiple values emitted and keep sophisticated async execution under very strict control.
function createCancellableMock(result, time) {
return Rx.Observable.create(function(observer) {
var done = false;
var token = setTimeout(function() {
if (result) {
console.log('almost done: ' + result);
observer.onNext(result);
observer.onCompleted();
} else {
observer.onError('_ERR_');
}
}, time);
// this will be called upon `disposed`
return function() {
console.log('disposing, done: ', done);
if (!done) {
clearTimeout(token);
}
}
})
}
var op1 = createCancellableMock('ok-1', 1000);
//var op2 = createCancellableMock('ok-2', 500);
var op2 = createCancellableMock(null, 500); // will be rejected
op1.zip(op2)
.catch(function(err) {
// it was disposed automatically :) hurray
console.log('Caught', err);
// return Rx.Observable.empty(); // swallowing
return Rx.Observable.throw(err); // throwing
})
.subscribe(function(vs) {
console.log('BOTH-OK', vs[0], vs[1])
},
function(err) {
console.error('Unhandled error', err);
},
function() {
console.log('Upon successful termination.')
}
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.all.js"></script>