web-dev-qa-db-ja.com

イベント駆動型環境では、「キャンセル」機能はそれ以降のすべてのイベントをどのように中止する必要がありますか?

私のiOSアプリでは、物事は主にイベントによって駆動されます。

  1. ユーザーがボタンを押す
  2. アプリは、iOSデバイスに接続される外部デバイスをリッスンします
  3. デバイスが検出される(接続される)と、Webサービスに対して非同期呼び出しが行われます(ユーザーの信頼性を確認するため)
  4. Webサービスは、ユーザーの信頼性(ブール値など)を返します。
  5. 別の非同期Webサービスは、ステージ2の信頼性のために呼び出されます
  6. 結果が返されます

私の質問は、ユーザーに「お待ちください、処理しています」というダイアログボックスを表示し、「キャンセル」ボタンを表示した場合、発生しているすべてのことをどのように(またはどのように)中止する必要がありますか?

これを行う1つの方法は、単に「userDidCancelEverything」というブール値を設定し、すべてのイベント駆動型メソッドがこれが真であるかどうかを確認することです。しかし、これは醜いようです。

2
Rowan Freeman

キャンセル機能を実装する方法は、アプリでイベントを実装する方法に明らかに依存します。たとえば、ユーザーがボタンをタップすると、結果のアクションによって、シリアル操作キューにいくつかの操作が追加される場合があります。最初の操作はデバイスが接続されるのを待ち、次の操作は認証Webサービスを呼び出します。そのようにすると、キャンセルボタンのアクションにより、操作キューの-cancelAllEventsメソッドが呼び出されてキューが空になり、操作に関連する状態が既知の状態にリセットされます。他のスキームを使用する場合も同様です。保留中のイベントを停止して削除してから、アプリを既知の状態に戻します。

1
Caleb

私は知っていましたが、これは古い質問ですが、興味深いことに十分であり、素晴らしい解決策があります。

これらの関数はすべて非同期であると仮定します。非同期パターンは「継続」と呼ばれます。

このような「継続」の可能な実装では、ディスパッチキューとブロックを直接使用できます。次に例を示します。

dispatch(my_queue, ^{
    [self connectDeviceWithCompletion:^(id result){
        if (result) {
            [self authenticateWithCompletion:^(id result){
                 if (result) {
                     [self performTwoFactorAuthWithCompletion:^(id result) {
                         if (result) {
                              ...
                         }
                     }];
                 }
            }];
        }
    }];
});

ご覧のとおり、ディスパッチとブロックの「継続」はすぐに混乱し、エラーチェックやキャンセルも追加しませんでした。

キューに入れられたブロックをキャンセルすることはそれほど簡単ではないことに注意してください。

エラー処理とキャンセルもサポートするよりわかりやすいソリューションは、「Promise」と呼ばれる概念を使用して実現できます(wiki Futures and promises ]を参照してください。PromiseはJavaScriptやその他の言語で非常に人気がありますが、 Promisesを実装するいくつかのObjective-Cライブラリについては、次の回答を参照してください: 成功:/失敗:ブロックvs完了:ブロック

RXPromise ライブラリを利用して次のように書くことができます。

RXPromise* finalResult = [self connectDevice]
.then(^(id result){
    // device successfully connected with result
    return [self authenticate];
}, nil)
.then(^id(id result){
    // authenticated successfully with result
    return [self performTwoFactorAuth];
}, nil)
.then(^id(id result){
    // successfully performed two-factor-authentication
    // do something 
    ...
}, nil)

[finalResult setTimeout:5*60]; // we cancel everything after 5 minutes

// Catch any error:
finalResult.then(nil, id(NSError* error){
    NSLog(@"Something went wrong, or user cancelled with error: %@", error);
});

self.finalResultPromise = finalResult;
...

後で:

ユーザーが最終的に結果が発生するのを待つとします。現在のビューから戻ると、これによりすべての非同期操作がキャンセルされます。

- (void) viewDidDisappear:(BOOL)animated {
    // we are no longer interested in the result         
    [self.finalResultPromise cancel];
    self.finalResultPromise = nil;

    [super viewDidDisappear:animated];
}

開示:私はRXPromsieの作者です;)

1
CouchDeveloper