我使用带有es7样式的babeljs async / await方法。我有一个主脚本,它将在所有返回promises的对象数组上调用异步方法。我使用Promise.all()来等待所有这些返回,但是,这些任务可能需要很长时间,如果超过阈值,我想中止所有这些任务,并且任务以适当的方式处理。
反正有没有完成这样的事情?目前,我能想到的唯一方法是生成一个进程,该进程执行调用这些方法的工作并等待它们全部解决,如果达到时间限制,它可以终止进程并执行它需要的任何处理。
更新:关于主脚本正在等待的这些方法的一些说明......他们可能正在进行一系列的操作(调用外部系统,在某处调用流文件等)而不执行可能被取消的单个操作独立。
更新#2:一些未经测试的半假代码
class Foo1 {
async doSomething() {
// call some external system
// copy some files
// put those files somewhere else (s3)
}
}
class Foo2 {
async doSomething() {
// Do some long computations
// Update some systems
}
}
class FooHandler {
constructor() {
this.fooList = [];
}
async start() {
await Promise.all(this.fooList.map(async (foo) => {
return await foo.doSomething();
}));
}
}
let handler = new FooHandler();
handler.fooList.push(new Foo1());
handler.fooList.push(new Foo2());
// if this call takes too long because of slow connections, errors, whatever,
// abort start(), handle it in whatever meaningful way, and continue on.
await handler.start();
答案 0 :(得分:9)
原生ES6承诺目前不支持直接取消。在很多地方一直都在谈论它,但它还没有。
由于本机承诺不支持它并且async / await适用于promises,因此目前没有内置的简单方法可以中止它。一种常见的方法是在创建返回promise的操作时使用令牌。
让我们说你已经宣传了XHR GET:
// simplification
function ajax(url){
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest;
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = reject;
xhr.send();
});
}
现在你要使用它:
async function foo(){
let result = await ajax("/myApi");
let result2 = await ajax("/myApi2?token=" + result);
}
现在,让我们说在某些情况下我们想取消AJAX,我们可以传递令牌:
function ajax(url, token = {}){
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest;
xhr.open("GET", url);
Object(token).cancel = () => { xhr.abort(), reject(); };
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = reject;
xhr.send();
});
}
这可以让你这样做:
async function foo(){
let token = {};
let req = ajax("/myApi", token); // note no await
// now let's say we want to abort the request since we don't
// need the data
token.cancel(); // this will abort the token
}
这种方法需要工作才能使用链接,幸运的是,使用ES6语法这并不是一件大事。祝你好运,编码愉快。
答案 1 :(得分:1)
如果你可以迁移到Typescript(其中类型是可选的,es6和一些es7特性支持开箱即用)而不是Babel并使用Bluebird promises,那么你可以实现你正在寻找的那种取消语义。 / p>
我创建了一个简单的模块,用一个支持Bluebird取消的帮助器取代了默认的Typescript __awaiter
助手:https://www.npmjs.com/package/cancelable-awaiter
有了它,您可以将aync / await语法与Bluebird为您提供的promise.cancel()
和promise.finally()
结合使用。
答案 2 :(得分:0)
这实际上取决于您需要使用的API。节点的大多数当前异步API方法都不容易“可中断”(readfileasync
等),除非您自己实现它们。
没有简单的方法可以轻松取消预定的调度。到目前为止,API并没有考虑到这一点。当API的低级实现不支持中止时,Promise也无能为力。
但在某些API中,您可以拦截流程“步骤”,例如data
事件上的流和“下一个滴答”实现。在那里你可以中止进一步的处理。 (Streams实际上非常适合实现可拦截IO的东西)
经典节点示例,其中每个请求提供输入“n”的斐波那契序列计算,逻辑通过“下一个滴答”实现。在那里,您实际上可以在计算上设置超时,服务器会自动启动长时间运行的请求:
var do_fibonacci_async = function(a,limiter,callback){
if(limiter.halt){
callback(limiter.msg);
}
else if(a <= 2){
callback(limiter.halt ? limiter.msg : 1);
}else{
process.nextTick(function(){
do_fibonacci_async(a - 1,limiter, function(val1){
do_fibonacci_async(a - 2,limiter, function(val2){
callback(limiter.halt ? limiter.msg : val1+val2);
});
});
});
}
}
exports.fibonacci_async = function(a,callback){
if(!a || isNaN(a)){
callback(new out("fibonacci", [], ""));
return;
}
var limiter = {halt:false, msg:"Too large to compute"};
setTimeout(function(){
limiter.halt = true;
},5000);
do_fibonacci_async(a,limiter,function(val){
callback(new out("fibonacci", [a], val));
});
}