Skip to content

ThrottleLast

1. 概要

ThrottleLast は、ソースシーケンスを一定期間ごとにサンプリングし、各期間中に受け取った最後の値を下流に発行するオペレーターです。

定期的な UI 更新やセンサーデータのサンプリングなど、一定間隔で最新の値を取得したい場合に適しています。

サンプリングのタイミングは overload によって異なります。TimeSpan 版は最初の値を受け取ってから一定時間後、sampler 版は外部 Observable が値を発行した時点、非同期関数版は samplerValueTask が完了した時点で、その期間中の最後の値を発行します。

2. シグネチャ

TimeSpan によるサンプリング

csharp
public static Observable<T> ThrottleLast<T>(
    this Observable<T> source,
    TimeSpan timeSpan)
csharp
public static Observable<T> ThrottleLast<T>(
    this Observable<T> source,
    TimeSpan timeSpan,
    TimeProvider timeProvider)

最初の値を受け取ると timeSpan のタイマーを開始し、タイマーが発火した時点で、その間に受け取った最後の値を発行します。発行後、次の値を受け取ると新しいタイマーが開始されます。

csharp
source.ThrottleLast(TimeSpan.FromSeconds(1))

ThrottleLast TimeSpan のマーブルダイアグラム

この例では、最初の値を受け取るとタイマーが開始され、その期間中に受け取った最後の値が下流に発行されます。区間内に値がなかった場合は何も発行されません。

サンプラー Observable によるサンプリング

csharp
public static Observable<T> ThrottleLast<T, TSample>(
    this Observable<T> source,
    Observable<TSample> sampler)

sampler が値を発行するたびに、その時点でソースから最後に受け取った値を発行します。

csharp
source.ThrottleLast(Observable.Interval(TimeSpan.FromSeconds(1)))

ThrottleLast sampler のマーブルダイアグラム

この例では、sampler が値を発行したタイミングで、その時点までにソースから受け取っている最後の値を発行します。前回のサンプリング以降にソース値がない場合は何も発行されません。

非同期関数によるサンプリング

csharp
public static Observable<T> ThrottleLast<T>(
    this Observable<T> source,
    Func<T, CancellationToken, ValueTask> sampler,
    bool configureAwait = true)

最初の値が届くと sampler 非同期関数を開始し、関数が完了した時点でその間に受け取った最後の値を発行します。

csharp
source.ThrottleLast(async (value, ct) =>
{
    await Task.Delay(TimeSpan.FromMilliseconds(500), ct);
})

ThrottleLast async sampler のマーブルダイアグラム

この例では、最初の値を受け取ると非同期 sampler が開始され、完了時点でその間に受け取った最後の値を発行します。発行後、次の値を受け取ると新しい非同期サンプリング期間が始まります。

overload の使い分け

overload使う場面
TimeSpan固定間隔のサンプリングで十分な場合(最も一般的)
TimeSpan, TimeProviderユニットテストで時間を制御したい場合
Observable<TSample>外部イベントでサンプリングタイミングを制御したい場合
Func<T, CancellationToken, ValueTask>値に応じてサンプリング間隔を動的に変えたい場合

3. サンプルコード

csharp
using R3;

// 1 秒ごとにセンサーの最新値を取得
sensorDataObservable
    .ThrottleLast(TimeSpan.FromSeconds(1))
    .Subscribe(value => UpdateDisplay(value));

// マウスの位置を 100ms ごとにサンプリング
mousePositionObservable
    .ThrottleLast(TimeSpan.FromMilliseconds(100))
    .Subscribe(pos => Console.WriteLine($"位置: ({pos.X}, {pos.Y})"));

// サンプラー Observable を使ったサンプリング
var tick = Observable.Interval(TimeSpan.FromSeconds(2));
source
    .ThrottleLast(tick)
    .Subscribe(x => Console.WriteLine($"サンプル: {x}"));

4. 補足

ThrottleFirst / Debounce との違い

オペレーター発行する値発行タイミング
ThrottleFirst最初の値値が来た直後
ThrottleLast最後の値期間の終了時
Debounce最後の値値が落ち着いた後

ThrottleLast は定期的に発行する点が Debounce と異なります。Debounce は値の到着がなくなるのを待ちますが、ThrottleLast は値が連続して届いていても定期的にサンプリングします。

フレームベースの版は ThrottleLastFrame を参照してください。