Pytanie Możliwość przerwania połączenia asynchronicznego


Używam babeljs z metodami async / await w stylu es7. Mam skrypt główny, który wywoła metodę asynchroniczną na tablicy obiektów, które wszystkie zwracają obietnice. Używam Promise.all (), aby czekać na wszystkich tych, którzy powrócą, jednak te zadania mogą zająć dużo czasu i jeśli przekroczą próg, chciałbym przerwać wszystkie z nich, a zadanie to obsłuży w odpowiedni sposób.

Czy mimo to jest coś takiego? Obecnie jedyny sposób, jaki mogę wymyślić, to zrobienie procesu, który wykonuje pracę wywoływania tych metod i czeka na ich rozwiązanie, a jeśli limit czasu zostanie osiągnięty, może zabić proces i zrobić to, czego potrzebuje.

Aktualizacja: Kilka wyjaśnień na temat tych metod, na które czeka główny skrypt ... Mogą wykonywać długie serie operacji (wywoływanie zewnętrznych systemów, przesyłanie strumieniowe plików gdzieś itp.) I niewykonywanie pojedynczego działania, które można anulować niezależnie.

Aktualizacja nr 2: Niektóre nietestowane kody semi-psuedo

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

11
2018-04-12 18:01


pochodzenie


Czy możesz opublikować swój kod? - Jeff Sloyer
Jest to doskonałe pytanie dotyczące prawdziwego problemu, dlaczego jest odrzucane? - Benjamin Gruenbaum
związane z: Jak anulować łańcuch obietnic EMCAScript6 (vanilla JavaScript) - Bergi


Odpowiedzi:


Natywne obietnice ES6 obecnie nie obsługują anulowania bezpośrednio. W wielu miejscach mówi się o tym cały czas, ale jeszcze go nie ma.

Odkąd lokalne obietnice go nie wspierają i asynchronicznie oczekują prac nad obietnicami, obecnie nie ma wbudowanego łatwego sposobu na przerwanie tego. Jednym z powszechnych podejść jest użycie tokena podczas tworzenia akcji zwracającej obietnicę.

Powiedzmy, że obiecałeś 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();
    });
}

Teraz chcesz go użyć:

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

Teraz, powiedzmy, że chcemy anulować AJAX w niektórych przypadkach, możemy przekazać token jako taki:

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

Pozwoli Ci to zrobić:

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
}

Takie podejście wymaga pracy z łańcuchem, na szczęście dzięki składni ES6 nie jest to tak wielka sprawa. Powodzenia i szczęśliwego kodowania.


8
2018-04-12 18:10



Dzięki, to jest dobra informacja. Jednak te metody asynchroniczne mogą spowodować dużo pracy (skopiuj plik z jednej lokalizacji do drugiej, wykonaj zewnętrzne połączenia, a następnie przesyłaj dane gdzieś (np. Do S3), a następnie wykonaj trochę prac porządkowych). Niestety nie jest to jedno połączenie, które robią te metody. - Greg A.
Więc zgrupuj tokeny. - Benjamin Gruenbaum


Jeśli możesz migrować do maszynopisu (w którym typy są opcjonalne, a es6 i niektóre funkcje es7 są obsługiwane po wyjęciu z pudełka) zamiast Babel i używać obietnic Bluebird, można uzyskać rodzaj semantyki anulowania, którego szukasz.

Stworzyłem prosty moduł, który zastępuje domyślny Maszynopis __awaiter pomocnik, który obsługuje anulacje Bluebird: https://www.npmjs.com/package/cancelable-awaiter

Dzięki niemu możesz użyć składni aync / await w połączeniu z promise.cancel() i promise.finally() który daje ci Bluebird.


1
2017-12-25 21:16





To naprawdę zależy od API, którego potrzebujesz. Większość obecnych metod Async API węzła nie jest łatwo "przerywalna" (readfileasync i tym podobnych), chyba że sam wykonasz ich własną implementację.

Nie ma łatwego sposobu na łatwe anulowanie zaplanowanej wysyłki. API do tej pory nie jest zbudowany z myślą o tym. Obietnice również nie mogą pomóc, gdy implementacje API niskiego poziomu nie obsługują przerywania.

Ale w niektórych interfejsach API można przechwytywać "kroki" procesu, takie jak strumienie włączone data zdarzenia i implementacje "następnego kleszcza". Tam możesz przerwać dalsze przetwarzanie. (Strumienie są rzeczywiście całkiem dobrym kandydatem do implementowania rzeczy IO z możliwością przechwytywania)

Klasyczny przykład węzła, w którym wyliczana jest sekwencja sygnału wejściowego "n" na żądanie, logika jest realizowana za pomocą "następnego tiku". Tam możesz ustawić limit czasu na obliczeniach, a serwer automatycznie kopie długo działające żądania:

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

0
2018-04-12 19:27