14

我正在使用 babeljs 和 es7 风格的 async/await 方法。我有一个主脚本,它将在所有返回承诺的对象数组上调用异步方法。我使用 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();
4

3 回答 3

13

Native ES6 promises currently do not support cancellation directly. There are talks about it all the time in many places but it's not there yet.

Since native promises don't support it and async/await works on promises, there is currently no built in easy way to abort it. One common approach is to use a token when creating the action returning a promise.

Let's say you've promisified 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();
    });
}

Now you want to use it:

async function foo(){
    let result = await ajax("/myApi");
    let result2 = await ajax("/myApi2?token=" + result);
}

Now, let's say we want to cancel the AJAX in some cases, we can pass a token as such:

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();
    });
}

This would let you do:

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
}

This approach needs work to work with chaining, luckily with ES6 syntax this is not as big of a deal. Good luck and happy coding.

于 2015-04-12T18:10:06.550 回答
1

如果您可以迁移到 Typescript(其中类型是可选的,并且开箱即支持 es6 和一些 es7 功能)而不是 Babel 并使用 Bluebird Promise,那么您正在寻找的那种取消语义可以实现。

我创建了一个简单的模块,它将默认的 Typescript__awaiter帮助器替换为支持 Bluebird 取消的模块:https ://www.npmjs.com/package/cancelable-awaiter

有了它,您可以结合使用 aync/await 语法promise.cancel()以及promise.finally()Bluebird 为您提供的语法。

于 2016-12-25T21:16:52.187 回答
0

这实际上取决于您需要使用的 API。大多数当前节点的异步 API 方法都不容易“中断”(readfileasync等等),除非您自己实现它们。

没有简单的方法可以轻松取消预定的调度。到目前为止,API 的构建并未考虑到这一点。当 API 的低级实现不支持中止时,Promise 也无济于事。

但是在某些 API 中,您可以截获流程“步骤”,例如data事件流和“下一个滴答”实现。在那里您可以中止进一步的处理。(流实际上是实现可拦截 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));
    });
}
于 2015-04-12T19:27:40.013 回答