Ability to abort asynchronous call

十年热恋 提交于 2019-12-20 20:28:52

问题


I'm using babeljs with es7 style async/await methods. I have a main script that will call a async method on an array of objects that all return promises. I use Promise.all() to wait for all of those to return, however, these tasks could take a long time and if they exceed a threshold I would like to abort all of them, and the task handle that in an appropriate way.

Is there anyway to accomplish such a thing? Currently the only way that I can think of is by spawning a process that does the work of calling these methods and waiting for them to all resolve, and if the time limit is reach, it can kill the process and do whatever handling it needs.

Update: Some clarification about these methods that the main script is waiting on... They might be doing a long series of operations (calling external systems, streaming files somewhere, etc) and not performing one single action that could be canceled independently.

Update #2: Some untested semi-psuedo code

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

回答1:


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.




回答2:


If you can migrate to Typescript (in which types are optional and es6 and some es7 features are supported out of the box) instead of Babel and use Bluebird promises, the kind of cancellation semantics you are looking for can be achieved.

I've created a simple module which replaces the default Typescript __awaiter helper with one that supports Bluebird cancellations: https://www.npmjs.com/package/cancelable-awaiter

With it you can use aync/await syntax in conjunction with promise.cancel() and promise.finally() which Bluebird gives you.




回答3:


This really depends on the API you need to use. Most of the current async API methods of node aren't easily "interruptible" (readfileasync and the likes), unless you yourself do your own implementation of them.

There is no easy way to easily cancel a scheduled dispatch. The API so far is not built with this thing in mind. Promises also can't help when low level implementations of API don't support aborting.

But in some API's you get to intercept process "steps", such as streams on data events and the "next tick" implementations. There you can abort the further processing. (Streams are actually pretty good candidate for implementing intercept-able IO stuff)

The classic node example, where a fibonacci sequence calculation of input "n" is served per request, the logic is implemented via "next tick". There you can actually set a timeout on the calculation and server automatically kicks long running requests:

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


来源:https://stackoverflow.com/questions/29592680/ability-to-abort-asynchronous-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!