Skip to content

ThrottleFirstLast

1. 概要

ThrottleFirstLast は、ソースシーケンスから最初に届いた値を即座に下流へ発行し、さらにスロットリング期間の終了時に最後に受け取った値も発行するオペレーターです。

ThrottleFirst の即時応答性と ThrottleLast の最新値保証を組み合わせたオペレーターです。最初の値に即座に反応しつつ、期間中に届いた最後の値も漏らさず下流に流したい場合に適しています。

2. シグネチャ

TimeSpan によるスロットリング

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

最初の値を即座に発行し、timeSpan 経過後に期間中の最後の値を発行します。

csharp
source.ThrottleFirstLast(TimeSpan.FromMilliseconds(500))

サンプラー Observable によるスロットリング

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

最初の値を即座に発行し、sampler が次の値を発行した時点で期間中の最後の値を発行します。

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

非同期関数によるスロットリング

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

最初の値を即座に発行し、sampler 非同期関数が完了した時点で期間中の最後の値を発行します。

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

overload の使い分け

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

3. マーブルダイアグラム

ThrottleFirstLast のマーブルダイアグラム

最初の値が即座に発行され、スロットリング期間が開始します。期間の終了時に、期間中に最後に受け取った値が追加で発行されます。最初の値と最後の値が同じ場合(期間中に 1 つしか値が届かなかった場合)は、1 つだけ発行されます。

4. サンプルコード

csharp
using R3;

// スクロールイベント:即座に応答しつつ、最終位置も捕捉
scrollEventObservable
    .ThrottleFirstLast(TimeSpan.FromMilliseconds(200))
    .Subscribe(pos => UpdateScrollPosition(pos));

// ドラッグ操作:開始位置と最新位置の両方を取得
dragObservable
    .ThrottleFirstLast(TimeSpan.FromMilliseconds(100))
    .Subscribe(pos => HandleDragPosition(pos));

// FakeTimeProvider を使ったテスト
var fakeTime = new FakeTimeProvider();
var results = new List<int>();

var subject = new Subject<int>();
subject
    .ThrottleFirstLast(TimeSpan.FromSeconds(1), fakeTime)
    .Subscribe(x => results.Add(x));

subject.OnNext(1);  // 即座に発行
subject.OnNext(2);
subject.OnNext(3);
// results: [1]

fakeTime.Advance(TimeSpan.FromSeconds(1));
// results: [1, 3] — 期間終了時に最後の値も発行

5. 補足

ThrottleFirst / ThrottleLast との違い

オペレーター発行する値応答性最新値の保証
ThrottleFirst最初の値のみ即座なし
ThrottleLast最後の値のみ期間終了時あり
ThrottleFirstLast最初 + 最後の値即座あり

ThrottleFirstLast は応答性と最新値の保証を両立しますが、1 つの期間あたり最大 2 つの値を発行する点に注意してください。

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