承诺被拒绝后停止运行进程 [英] Stop running processes after a Promise is rejected
问题描述
我正在使用下面的代码,它们工作正常,但是问题是当我遇到错误时,我希望它停止所有其他promise.例如,如果chi.getCommand(val1, val2)
将发送拒绝,而我进入了异常捕获,我想取消对chss.exe
和app.getStatus(12);
的承诺,我该如何实现?
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, val2)
, will send a reject and I got to the exception catch, I want to cancel the promises for chss.exe
and app.getStatus(12);
How can I achieve that?
var start = Promise.all([
chi.getCommand(val1, val2),
chi.findAndUpdateCustomer()
]).spread(function (command, customer) {
return chss.exe(runnableDoc, command, customer)
.delay(10)
.then(function (val) {
if (val) console.log(val);
return app.getStatus(12);
});
}).catch(function (err) {
// catch and handle errors and when it come to here I want it to stops all the chain above
});
简而言之,这是get命令的代码:
This is the code of get command in short:
function getCommand(method, cmd) {
return new Promise(function (resolve, reject) {
...
child.stderr.on('data', function (data) {
console.log('stderr: here!' + data);
reject(data);
});
}
将打印控制台日志 stderr:此处!,以便调用解决方法!
The console log stderr: here! are printed so the resolve are called!
UPDATE1
唯一阻止getStatus的事情是当我放下process.exit(1)
时,但这会杀死所有进程,我只是想停止getCommand函数的所有链,以防万一我到达catch块,
The only thing which stops the getStatus is when I put the process.exit(1)
But this kill all the process, I just want to stop all the chain of the function getCommand in case Im arriving to the catch block,
- 有办法吗?
- blueBird中的错误吗?我使用"bluebird":"2.9.34"
function getCommand(method,cmd){ 返回新的Promise(函数(解决,拒绝){
function getCommand(method, cmd) { return new Promise(function (resolve, reject) {
var spawn = require('child_process').spawn;
var ls = spawn("cmdbug",["/c","npm install express --save"]);
ls.on('error', function (err) {
console.log(err);
reject(err);
});
我得到的错误是
{[错误:产生cmdr ENOENT] 代码:"ENOENT", errno:"ENOENT", syscall:"spawn cmdbug", 路径:"cmdr", spawnargs:['/g','npm install express --save']} {[错误:产生cmdbug ENOENT] 代码:"ENOENT", errno:"ENOENT", syscall:"spawn cmdbug", 路径:"cmdr", spawnargs:['/g','npm install express --save']} 子进程失败,代码为-4058
{ [Error: spawn cmdr ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn cmdbug', path: 'cmdr', spawnargs: [ '/g', 'npm install express --save' ] } { [Error: spawn cmdbug ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn cmdbug', path: 'cmdr', spawnargs: [ '/g', 'npm install express --save' ] } Child process failed with code -4058
getStatus的过程仍在写入控制台中.
And still the process of getStatus is writing to the console.
我不用于测试的代码是:
The code which I use and not for testing is:
getCommand 是引发错误!
var start= function () {
return new Promise.all([
childP.getChildProcessCommand(val1, val2),
childP.findAndUpdateCustomer()
]).spread(function (cmd, updated) {
//Execute child process
return Promise.all([
childP.getCommand('spawn', cmd),
app.getStatus(51000,10,1);
]).catch(function (err) {
// catch and handle errors
console.log("An error occur: " + err);
return;
})
}).catch(function (err) {
// catch and handle errors
console.log("An error occur: " + err);
return;
})
}();
检查状态代码为:
// Returns a promise that resolves when the port is open
checkPortStatus: function(port, host){
return new Promise((resolve, reject) => {
portscanner.checkPortStatus(port, host, function(error, status) {
if(error)
reject(error);
else if(status === 'open')
resolve(status);
else
reject(new Error('Port is not open'));
});
});
},
// THE API function
getStatus: function(port, retriesLeft) {
const TIME_BETWEEN_CHECKS = 1000;
const HOST = '127.0.0.1';
const RETRIES = 20;
retriesLeft = retriesLeft === void 0 ? RETRIES : retriesLeft;
if(!port) throw new Error('Port is required');
if(retriesLeft === 0) Promise.reject('Timed Out');
return new Promise((resolve, reject) => {
// If it rejects, we do added work.
this.checkPortStatus(port, host).then(resolve, error => {
console.log("Waiting for port " + port + " attempt: " + retry);
setTimeout(() => {
this.getStatus(port, retriesLeft - 1).then(resolve, reject);
}, TIME_BETWEEN_CHECKS);
});
});
}
而且我在控制台中看到错误,但仍然看到控制台日志 以下尝试10次. console.log(等待端口" +端口 +尝试:" +重试);
And I see the error in the console and still see the console log of the following for 10 attempts. console.log("Waiting for port " + port + " attempt: " + retry);
UPDATE2 尝试更改时由于第二个选项中的@artur建议,在回叫调用中出现错误,错误是:
UPDATE2 When trying to change As @artur suggest in the second option I got error in the recoursive call the error is:
TypeError:无法读取未定义的属性'then'
TypeError: Cannot read property 'then' of undefined
这是我尝试过的:
getStatus: function(port, retriesLeft) {
const TIME_BETWEEN_CHECKS = 1000;
const HOST = '127.0.0.1';
const RETRIES = 20;
retriesLeft = retriesLeft === void 0 ? RETRIES : retriesLeft;
if(!port) throw new Error('Port is required');
if(retriesLeft === 0) Promise.reject('Timed Out');
var promise = new Promise((resolve, reject) => {
// If it rejects, we do added work.
this.checkPortStatus(port, host).then(resolve, error => {
console.log("Waiting for port " + port + " attempt: " + retry);
setTimeout(() => {
//The error in the following recursive call
this.getStatus(port, retriesLeft - 1).then(resolve, reject);
}, TIME_BETWEEN_CHECKS);
}).catch(function (error) {
return reject(error);
});
return {
promise:promise,
cancel: function() {
console.log('cancelling');
clearTimeout(token);
}
}
});
});
}
推荐答案
正如@Esailija指出的那样,bluebird
具有内置的取消机制-确实很棒,可以肯定完全可以异步计算.
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>
通常,诺言是很好的,但是它们没有提供开箱即用的取消机制.在某些情况下(例如 https://github.com/whatwg/fetch/issues/27 ),在您的情况下,取消选项也非常方便.唯一有效的选项是自己添加.
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.
我将问题简化到最低限度,并使它可在浏览器中运行.
以下方法的缺点是,取消承诺后,承诺将永远不会resolve
或reject
-一般情况下,肯定不会
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>
这篇关于承诺被拒绝后停止运行进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!