もう 1 つの解決策は、いくつかのサードパーティ ライブラリで利用できるPromiseを使用することです。私はPromises/A+ 仕様を実装するRXPromiseの作成者です。
しかし、他に少なくとも 2 つの Objective-C 実装があります。
Promise は、非同期メソッドまたは操作の最終的な結果を表します。
-(Promise*) doSomethingAsync;
promise は、完了ハンドラを完全に置き換えるものです。さらに、その明確な仕様と基礎となる設計により、非常に便利な機能がいくつかあり、かなり複雑な非同期問題を特に簡単に処理できます。
最初に行う必要があるのは、完了ハンドラーを使用して非同期メソッドをPromise を返す非同期メソッドにラップすることです: (意図的に、メソッドは最終的な結果と潜在的なエラーをより便利な完了ハンドラーで返します)
例えば:
- (RXPromise*) downloadAppInfo {
RXPromise* promise = [RXPromise new];
[self downloadAppInfoWithCompletion:^(id result, NSError *error) {
if (error) {
[promise rejectWithReason:error];
}
else {
[promise fulfillWithValue:result];
}
}];
return promise;
}
ここで、元の非同期メソッドがプロミスの「リゾルバー」になります。promise は、タスクの最終的な結果または失敗の理由を指定して、実行 (成功) または拒否 (失敗) することができます。promise は、非同期操作またはメソッドの最終的な結果を保持します。
ラッパーは非同期メソッドであり、「保留中」状態の promise をすぐに返すことに注意してください。
最後に、メソッドまたはプロパティに成功ハンドラーと失敗ハンドラーを「登録」することにより、最終的な結果を取得します。then
周りのいくつかの promise ライブラリはわずかに異なりますが、基本的には次のようになります。
`promise.then( <success-handler>, <error-handler> )`
Promise/A+ 仕様には最小限の API があります。上記は、基本的に Promise/A+ 仕様を実装するための 1 つの必要条件です。多くの単純なユース ケースでは、多くの場合、これで十分です。
ただし、場合によってはもう少し必要な場合もあります。たとえば、一連の非同期メソッドを「待機」し、すべてが完了したときに何かを実行する必要がある OP の問題などです。
幸いなことに、Promise は、より洗練されたヘルパー メソッドを非常に簡単に構築するための理想的な基本構成要素です。
多くの Promise ライブラリはユーティリティ メソッドを提供します。たとえば、all
Promise を返し、Promise の配列を入力として受け取る非同期メソッドであるメソッド (または類似のメソッド) です。返された promise は、すべての操作が完了するか、いずれかが失敗したときに解決されます。次のようになります。
最初に promise の配列を作成し、同時にすべての非同期タスクを並行して開始します。
NSArray* tasks = @[
[self downloadAppInfo],
[self getAvailableHosts],
[self getAvailableServices],
[self getAvailableActions],
];
注: ここでは、タスクは既に実行されています (完了する可能性があります)。
ここで、上記のことを正確に実行するヘルパー メソッドを使用します。
RXPromise* finalPromise = [RXPromise all:tasks];
最終結果を取得します。
finalPromise.then(^id( results){
[self doSomethingWithAppInfo:results[0]
availableHosts:results[1]
availableServices:results[2]
availableActions:results[3]];
return nil;
}, ^id(NSError* error) {
NSLog(@"Error %@", error); // some async task failed - log the error
});
返された promise がメソッドで何らかの方法で解決されると、成功または失敗のハンドラーが呼び出されることに注意してくださいall:
。
返された promise ( finalPromise ) は、次の場合に解決されます。
- すべてのタスクが成功したとき、または
- 1 つのタスクが失敗しました
ケース 1) の場合、最終的な promise は、対応する各非同期タスクの結果を含む配列で解決されます。
ケース 2) の場合、最終的な promise は失敗した非同期タスクのエラーで解決されます。
(注: 利用可能ないくつかのライブラリはここで異なる場合があります)
RXPromise ライブラリには、いくつかの追加機能があります。
プロミスの非巡回グラフでキャンセル信号を転送する洗練されたキャンセル。
ハンドラーが実行されるディスパッチ キューを指定する方法。キューは、共有リソースへのアクセスを同期するために使用できます。
self.usersPromise = [self fetchUsers];
self.usersPromise.thenOn(dispatch_get_main_queue(), ^id(id users) {
self.users = users;
[self.tableView reloadData];
}, nil);
他のアプローチと比較すると、このdispatch_group
ソリューションはスレッドをブロックするという問題があります。これは完全に「非同期」ではありません。また、キャンセルを実装することは、不可能ではないにしても非常に複雑です。
解決策はNSOperation
複雑な祝福のようです。NSOperations
すでに があり、依存関係を定義するときに考慮する必要がある完了ハンドラーがない場合にのみ、エレガントになる可能性があります。そうでない場合は、雑然として複雑になります。
これまでに言及されていない別のソリューションは、Reactive Cocoaです。私見、それは事実上あらゆる複雑さの非同期問題を解決できる素晴らしいライブラリです。ただし、学習曲線が非常に急であり、アプリに多くのコードを追加する可能性があります。そして、つまずいた非同期問題の 90% は、キャンセル可能な promise で解決できると思います。さらに複雑な問題がある場合は、RAC を検討してください。