ネイティブ非同期APIが用意されていないI/O処理を非同期処理して待機したい。
やり方、これで合ってるのかなぁ……?
単一値の場合は Task
で包んで「はいどーぞ」なのだけど、複数値となるとこれは IObservable<T>
の出番?
提供されているのは IEnumerable<T>
を返すメソッドとする。
同期バージョン
public void DoSomething()
{
// Workspace.GetPendingChangesEnumerable は各要素を取得するのに時間がかかるため同期処理すると画面がフリーズする
IEnumerable<PendingChange> pendingChanges = workspace.GetPendingChangesEnumerable();
foreach (var pendingChange in pendingChanges)
{
_files.Add(pendingChanges);
}
// なんか取得後に処理したいことがあるとする(ここではフィールドの書き換え)
_completed = true;
}
これを処理するために ToObservable
で非同期シーケンスにして、await
で待機する。
拡張メソッドを用意する
public static class WorkspaceExtensions
{
public static IObservable<PendingChange> GetPendingChangesObservable(this Workspace workspace)
{
return workspace.GetPendingChangesEnumerable().ToObservable(Scheduler.Default);
}
}
非同期バージョン
public async Task DoSomething()
{
// IObservable<T> にすれば各要素を非同期に取得できる
// ※ Task<IEnumerable<T>> とは根本的に違う点に注意
IObservable<PendingChange> pendingChanges = workspace.GetPendingChangesObservable();
// IObservable<T> は awaitable (ToTask して await するのと同じ。Last が待機される)
// await によって購読が始まるため Do メソッドでよい
await pendingChanges.Do(pendingChange =>
{
_files.Add(pendingChanges);
// 空の場合を考慮して DefaultIfEmpty しないと例外になる(await は Last を取るため)
}).DefaultIfEmpty();
// 全要素の処理が完了後にここが呼ばれる
_completed = true;
}
呼び出し元を考えなければ Subscribe
の onCompleted
で処理すればいいんだけど、呼び出し元には Task
で返したかったのでこうなりました。
うーん、不安……