2015-04-12 26 views
10

Sto usando babeljs con i metodi async/await in stile es7. Ho uno script principale che chiamerà un metodo asincrono su una serie di oggetti che restituiscono tutte le promesse. Io uso Promise.all() per attendere il ritorno di tutti quelli, tuttavia, queste attività potrebbero richiedere molto tempo e se superano una soglia vorrei interromperle tutte e il task gestirà ciò in modo appropriato.Possibilità di annullare la chiamata asincrona

Esiste comunque una cosa del genere? Attualmente l'unico modo a cui posso pensare è di generare un processo che faccia il lavoro di chiamare questi metodi e di attendere che tutti si risolvano, e se il limite di tempo è raggiunto, può uccidere il processo e fare qualsiasi cosa abbia bisogno.

Aggiornamento: alcuni chiarimenti su questi metodi su cui lo script principale è in attesa ... Potrebbero fare una lunga serie di operazioni (chiamare sistemi esterni, eseguire lo streaming di file da qualche parte, ecc.) E non eseguire una singola azione che potrebbe essere annullato indipendentemente

Aggiornamento # 2: Alcuni codice di semi-pseudo non testati

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

Potresti pubblicare il tuo codice? –

+1

Questa è un'ottima domanda su un problema reale, perché è downvoted? –

+0

correlati: [Come cancellare una catena di promessa EMCAScript6 (vanilla JavaScript)] (http://stackoverflow.com/q/29478751/1048572) – Bergi

risposta

7

Native ES6 promette attualmente non supporta direttamente la cancellazione. Ne parliamo continuamente in molti posti, ma non c'è ancora.

Poiché le promesse native non lo supportano e async/await funziona sulle promesse, non esiste attualmente un modo semplice per abortire. Un approccio comune consiste nell'utilizzare un token quando si crea un'azione che restituisce una promessa.

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

ora si vuole usarlo:

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

Ora, diciamo che vogliamo cancellare l'AJAX, in alcuni casi, si può passare un token come ad esempio:

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

Ciò permetterà di fare:

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 
} 

Questo approccio richiede lavoro per lavorare con il concatenamento, per fortuna con la sintassi ES6 questo non è un grosso problema. Buona fortuna e buona programmazione.

+0

Grazie, questa è una buona informazione. Tuttavia, questi metodi asincroni potrebbero finire per fare un sacco di lavoro (copiare un file da una posizione a un'altra, effettuare alcune chiamate esterne, quindi eseguire lo streaming dei dati da qualche parte (come la pubblicazione su s3) e quindi eseguire alcune operazioni di pulizia). Sfortunatamente non è una singola chiamata che stanno facendo questi metodi. –

+0

Quindi aggregare i token. –

0

Questo dipende molto dall'API che è necessario utilizzare. La maggior parte degli attuali metodi API asincroni di nodo non sono facilmente "interrompibili" (readfileasync e simili), a meno che tu stesso non li esegui da solo.

Non esiste un modo semplice per annullare facilmente una spedizione programmata. L'API finora non è costruita pensando a questa cosa. Anche le promesse non possono essere d'aiuto quando implementazioni di API di basso livello non supportano l'interruzione.

Ma in alcune API è possibile intercettare i "passaggi" del processo, ad esempio gli stream sugli eventi data e le implementazioni "next tick". Qui puoi interrompere l'ulteriore elaborazione. (Gli stream sono in realtà candidati abbastanza buoni per l'implementazione di routing IO intercettabili)

L'esempio di nodo classico, in cui viene eseguito un calcolo della sequenza di fibonacci di input "n" per richiesta, la logica viene implementata tramite "tick successivo".Ci si può effettivamente impostare un timeout sul calcolo e server di calci automaticamente le richieste esecuzione prolungata:

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

Se è possibile migrare a Carattere tipografico (in cui i tipi sono opzionali e ES6 e alcune caratteristiche ES7 sono supportate out of the box) invece di Babel e usa le promesse di Bluebird, puoi ottenere il tipo di semantica di cancellazione che stai cercando.

ho creato un semplice modulo che sostituisce il dattiloscritto __awaiter aiutante di default con uno che supporta le cancellazioni Bluebird: https://www.npmjs.com/package/cancelable-awaiter

Con esso è possibile utilizzare aync/await sintassi in collaborazione con promise.cancel() e promise.finally() che Bluebird ti dà.

Problemi correlati