web-dev-qa-db-ja.com

一連の非同期呼び出しとそれに続くクリーンアップのパターンはありますか?

私は完了する必要がある2つの非同期タスクを持っています-それらが「パイを作る」(make)と「パイを焼く」(bake)だと言います。私はまた、すべてが終わった後、「台所を掃除する」(cleanup)を掃除する必要があります。 bakemakeに依存しているので、物事が完了したらcleanupにしたいです。ただし、いくつかの複雑な問題があります。

  • ナイスな人がパイをすでにmade持っている場合、私がしなければならないことはbakeだけです。
  • makeの間に問題が発生した場合-このパイ事業全体に飽き飽きしている可能性があります-cleanupする必要があります。
  • bakeの間に問題が発生した場合-オーブンが爆発します-cleanupも必要です。

最初のパスは次のようになります。

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)

これは悪くありませんが、このアプローチの私の主な問題は、フローからの単一の出口点がないことです。 cleanupはたくさんの場所から呼び出されます。エラー処理がより複雑になり、非同期呼び出しをチェーンに追加するにつれて、追跡するフローからの出口点がますます多くなります。それらのいずれかでcleanupに電話できなかった場合、その結果、使用できないキッチンになります。

基本的に、私は非同期呼び出しのdo/catch/finallyのようなものを探しています。私が思いつくことができる唯一のものはキューです-必要なタスクのみをキューに入れ、最後にクリーンアップをキューに入れます-しかし、このインスタンスのような2つのタスクだけでは少し重すぎるようです。この問題に定評のあるパターンはありますか?

3
Chaosed0

慣用的な q 1のような約束チェーンを使用できます。

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

呼び出しのツリーとの主な違いは、追跡するのが非常に直感的であるということです。エントリポイントが何であるかは問題ではなく、ステップのシーケンスは問題ではありません。クリーンアップは、シーケンスが完了すると無条件に行われます。

6
l0b0

何故なの

  • bakeへの呼び出しなしで関数makeおよびcleanUpを実装します(ただし、特定の例外のような一貫したエラー信号)

  • 関数ラッパー(「デコレータ」)を実装します。これは、任意の関数をパラメーターとして取り、非同期に実行し、エラー例外をキャッチして、それに応じてcleanupを呼び出しますか?

次に、bakeおよびmake(および他の同等のタスク)が開始されるコード内の場所は、ラッパーを使用してタスクを実行するだけです。最後のcleanup(null)bakeによって直接呼び出されるのではなく、呼び出し元によって呼び出されます(たとえば、エラーなしに完了したすべてのタスクのイベントを取得したとき)。

3
Doc Brown

Cleanupメソッドは、makeおよびbakeコマンドチェーンのデコレータとして機能します。この場合、デコレータはクリーンアップの目的を果たします。コマンドチェーンを実行すると、クリーンアップがキャッチしてクリーンアップするまで、エラーがチェーンを上に移動します。

ベイクは作成に依存し、エラー処理も作成の結果に依存するため、このチェーンの一部として非同期で追加する理由はありません。チェーン全体を非同期で実行する場合(複数のパイを一度に作成してベイク処理する場合)、最上部で非同期コールアウトを作成し、個々のコールアウトではなくコールチェーン全体のタスク結果を確認します。

これがチェーンです(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());

クリーンアップデコレータ:

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

パイを作る:

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

パイを焼く:

 public class BakePie : ICommand<Pie>
    {
        public void Execute(Pie pie)
        {
            //TODO: Bake Pie
        }
    }
2
Jon Raynor