Esiste uno schema per una catena di chiamate asincrone, seguita da una pulizia?

4

Ho due compiti asincroni che devono essere completati - diciamo che sono "fare una torta" ( make ) e "cuocere una torta" ( bake ). Ho anche bisogno di ripulire dopo aver fatto tutto - "pulire la cucina" ( cleanup ). bake dipende da make , e voglio cleanup quando le cose sono fatte. Tuttavia, ci sono alcune complicazioni:

  • Se qualche bella persona ha già made della torta, tutto ciò che devo fare è bake .
  • Se si verifica un problema durante make - forse mi stanco di tutto questo business pie - ho bisogno di cleanup .
  • Se si verifica un problema durante bake - il forno esplode - Devo anche cleanup .

Il mio primo passaggio sembra qualcosa del genere:

func pieTime()
    if !pie.isMade()
        make()
    else if !pie.isBaked()
        bake()
    else
        cleanup(null)

func make()
    makeThePie(completion: {
        if pie.hasError()
            cleanup(error)
        else
            bake()
    })

func bake()
    bakeThePie(completion: {
        if pie.hasError()
            cleanup(error)
        else
            cleanup(null)
    })

func cleanup(error)
    if error != null
        shout("what the ***: %s", error.string())
    destroy(pie)

Questo non è male, ma il mio problema principale con questo approccio è che non esiste un singolo punto di uscita dal flusso. cleanup viene chiamato da un sacco di posti. Man mano che la gestione degli errori diventa più complicata e si aggiungono più chiamate asincrone alla catena, ci sono sempre più punti di uscita dal flusso per tenere traccia di. Se non riesco a chiamare cleanup in nessuna di esse, il risultato è una cucina inutilizzabile.

Fondamentalmente, sto cercando qualcosa come un do / catch / finally per le chiamate asincrone. L'unica cosa che potrei inventare è una coda: metti in coda solo le attività di cui hai bisogno e fai una coda di pulizia alla fine, ma sembra un po 'troppo pesante per due soli compiti, come questa istanza. C'è un modello ben definito per questo problema?

    
posta Chaosed0 21.04.2017 - 19:59
fonte

3 risposte

6

Puoi utilizzare una catena di promesse come la q idiomatica, approssimativamente:

doSomethingThatReturnsAPromise
    .then(function (result, error) {
        if (error) {
            [the previous command failed in some way; handle it or throw.]
        }
    })
    .then([same again])
    […]
    .finally(function() {
        [global cleanup]
    })

La principale differenza da un albero di chiamate è che è molto intuitivo da seguire - non c'è dubbio su quale sia il punto di ingresso, non c'è alcuna questione sulla sequenza dei passaggi e la pulizia avviene incondizionatamente una volta eseguita la sequenza.

    
risposta data 21.04.2017 - 20:18
fonte
3

Perché no

  • implementa le funzioni bake e make senza alcuna chiamata a cleanUp (ma segnalazione coerente dell'errore come un'eccezione specifica)

  • implementa un wrapper funzionale (un "decoratore") che accetta una funzione arbitraria come parametro, lo esegue in modo asincrono, rileva qualsiasi eccezione di errore e chiama cleanup di conseguenza?

Quindi la posizione nel codice dove bake e make (e altre attività equivalenti) sono state avviate, è sufficiente utilizzare il wrapper per eseguire le attività. Il cleanup(null) finale non dovrebbe essere chiamato da bake direttamente, ma dal chiamante (ad esempio, quando ottiene l'evento per tutte le attività che si sono completate senza errori).

    
risposta data 21.04.2017 - 21:20
fonte
2

Il metodo di pulizia può fungere da decoratore per la catena di comandi make e bake. In questo caso, il decoratore ha lo scopo di ripulire. Mentre la catena di comando viene eseguita, qualsiasi errore si sposta sulla catena fino a quando la pulitura non viene catturata e pulita.

Non vi è alcun motivo per aggiungere asincrono come parte di questa catena poiché la cottura dipende dal fare e la gestione degli errori dipende anche dal risultato della fabbricazione e, eventualmente, della cottura. Se si volesse eseguire l'intera catena asincrona (preparare e cuocere più torte contemporaneamente, quindi in cima farei un richiamo asincrono e poi guarderò il risultato dell'attività dell'intera catena di chiamate in contrapposizione alle chiamate individuali.

Ecco la catena (in C #):

var command = new PieCleanup(new MakePie(new BakePie()));
//You could run this async if many pies to make and bake
command.Execute(new Pie());

The Cleanup Decorator:

    public class PieCleanup : ICommand<Pie>
    {
        private readonly ICommand<Pie> _command;

        public PieCleanup(ICommand<Pie> command)
        {
            _command = command;
        }

        public void Execute(Pie pie)
        {
            try
            {
                _command.Execute(pie);
            }
            catch (Exception)
            {
                //TODO:  Cleanup
            }
        }
    }

Crea torta:

 public class MakePie : ICommand<Pie>
    {
        private readonly ICommand<Pie> _command;

        public MakePie(ICommand<Pie> command)
        {
            _command = command;
        }

        public void Execute(Pie pie)
        {
            //TODO: Make Pie
            //Now Run the next command (bake)
            _command.Execute(pie);
        }
    }

Bake Pie:

 public class BakePie : ICommand<Pie>
    {
        public void Execute(Pie pie)
        {
            //TODO: Bake Pie
        }
    }
    
risposta data 21.04.2017 - 22:08
fonte

Leggi altre domande sui tag