Reactive Operators
Use this catalog to look up the exact shape of a reactive member: every operator, factory, subscribe overload, lifetime helper, Unity frame source, provider and OnityTask bridge that Onity ships for IOnityObservable<T>. The Reactive guide explains when to use what.
Subject<T>, ReactiveProperty<T>, every operator below and the message bridge Observe<T>() return IOnityObservable<T>, so one chain composes over local streams, shared state and messages. The synchronous, time, async and thread-pool operators live in Onity.Reactive and are engine-free. Frame scheduling (ObserveOnMainThread, Delay), the frame and timer sources and the lifetime helpers live in Onity.Unity.Reactive; the OnityTask bridges live in Onity.Unity.Async. A synchronous operator allocates when it is subscribed and forwards each value without allocating; the core uses no System.Linq and no third-party runtime dependency.
Conventions:
- Signature omits the extension receiver; the receiver is the source stream unless noted.
- Kind:
Syncforwards synchronously;Timewaits on anOnityTimeProvider;Asyncawaits a delegate or a task;Threaduses the .NET thread pool;Framere-posts onto a Unity loop. - Every operator throws
ArgumentNullExceptionfor a null source or delegate.
Every Subscribe returns an IDisposable; dispose it, or scope it with AddTo(this), TakeUntilDisable(this), AddTo(compositeDisposable) or AddTo(scope).
Filtering and projection
| Operator | Signature | Namespace | Description | Kind |
|---|---|---|---|---|
Where | Where<T>(Predicate<T> predicate) -> IOnityObservable<T> | Onity.Reactive | Forwards the values for which predicate returns true. | Sync |
Select | Select<TSource, TResult>(Func<TSource, TResult> selector) -> IOnityObservable<TResult> | Onity.Reactive | Projects each value through selector. | Sync |
DistinctUntilChanged | DistinctUntilChanged<T>(IEqualityComparer<T> comparer = null) -> IOnityObservable<T> | Onity.Reactive | Suppresses consecutive duplicates; EqualityComparer<T>.Default when comparer is null. | Sync |
Skip | Skip<T>(int count) -> IOnityObservable<T> | Onity.Reactive | Drops the first count values. count == 0 returns the source; a negative count throws ArgumentOutOfRangeException. | Sync |
SkipWhile | SkipWhile<T>(Predicate<T> predicate) -> IOnityObservable<T> | Onity.Reactive | Drops leading values while predicate is true, then forwards everything. | Sync |
Take | Take<T>(int count) -> IOnityObservable<T> | Onity.Reactive | Forwards the first count values, then disposes the upstream subscription. count == 0 returns OnityObservable.Empty<T>(); negative throws. | Sync |
TakeWhile | TakeWhile<T>(Predicate<T> predicate) -> IOnityObservable<T> | Onity.Reactive | Forwards values while predicate is true; the first false value disposes the upstream subscription. | Sync |
StartWith | StartWith<T>(T initialValue) -> IOnityObservable<T> | Onity.Reactive | Emits initialValue on subscribe, then the source values. | Sync |
State and combination
| Operator | Signature | Namespace | Description | Kind |
|---|---|---|---|---|
Scan | Scan<TSource, TState>(TState seed, Func<TState, TSource, TState> accumulator) -> IOnityObservable<TState> | Onity.Reactive | Folds each value into a running state and emits the state after every value. | Sync |
Pairwise | Pairwise<T>() -> IOnityObservable<OnityPair<T>> | Onity.Reactive | Emits each value with the previous one as OnityPair<T> (Previous, Current); the first value only primes the pair. | Sync |
Buffer (count) | Buffer<T>(int count) -> IOnityObservable<IReadOnlyList<T>> | Onity.Reactive | Emits a new list every count values. count <= 0 throws ArgumentOutOfRangeException. | Sync |
Merge | Merge<T>(params IOnityObservable<T>[] others) -> IOnityObservable<T> | Onity.Reactive | Forwards every value from the receiver and all others. No others returns the receiver; a null element throws. | Sync |
CombineLatest | CombineLatest<TFirst, TSecond, TResult>(IOnityObservable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) -> IOnityObservable<TResult> | Onity.Reactive | Emits resultSelector(latestFirst, latestSecond) whenever either source emits, once both have a value. Two sources only. | Sync |
Sample | Sample<T, TSignal>(IOnityObservable<TSignal> sampler) -> IOnityObservable<T> | Onity.Reactive | Emits the latest source value each time sampler emits, once a source value exists. | Sync |
Time
These wait on an OnityTimeProvider. When timeProvider is null they use OnityTimeProvider.System (wall-clock Task.Delay); in gameplay pass one of OnityTimeProviders so the wait follows a Unity loop and time mode. A non-positive interval or timeSpan throws ArgumentOutOfRangeException.
| Operator | Signature | Namespace | Description | Kind |
|---|---|---|---|---|
Debounce | Debounce<T>(TimeSpan dueTime, OnityTimeProvider timeProvider = null) -> IOnityObservable<T> | Onity.Reactive | Emits the last value after a quiet window of dueTime. TimeSpan.Zero returns the source; negative throws. | Time |
ThrottleLast | ThrottleLast<T>(TimeSpan interval, OnityTimeProvider timeProvider = null) -> IOnityObservable<T> | Onity.Reactive | Trailing edge: emits the latest value once per interval while values flow. | Time |
Throttle | Throttle<T>(TimeSpan interval, OnityTimeProvider provider = null) -> IOnityObservable<T> | Onity.Reactive | Leading edge: emits the first value, then drops values until interval elapses. | Time |
Buffer (time) | Buffer<T>(TimeSpan timeSpan, OnityTimeProvider provider = null) -> IOnityObservable<IReadOnlyList<T>> | Onity.Reactive | Emits the values gathered in each timeSpan window; an empty window emits nothing. | Time |
ThrottleLast is the periodic “latest value” tick; Throttle fires at once and then ignores the rest of a burst. UniRx’s Throttle corresponds to Debounce.
Async
SelectAwait and WhereAwait run their delegate one value at a time, in order, inside Task.Run, and forward on the thread that completed it. Hop back with ObserveOnMainThread() before touching UnityEngine. A delegate that throws ends the subscription without notifying the subscriber.
| Operator | Signature | Namespace | Description | Kind |
|---|---|---|---|---|
SelectAwait | SelectAwait<TSource, TResult>(Func<TSource, CancellationToken, ValueTask<TResult>> selector) -> IOnityObservable<TResult> | Onity.Reactive | Sequential async projection; the token is canceled when the subscription is disposed. | Async |
WhereAwait | WhereAwait<T>(Func<T, CancellationToken, ValueTask<bool>> predicate) -> IOnityObservable<T> | Onity.Reactive | Sequential async filter. | Async |
TakeUntil (token) | TakeUntil<T>(CancellationToken cancellationToken) -> IOnityObservable<T> | Onity.Reactive | Stops forwarding when the token is canceled; an already canceled token returns an empty stream. | Async |
TakeUntil (task) | TakeUntil<T>(Task untilTask) -> IOnityObservable<T> | Onity.Reactive | Stops forwarding when untilTask completes, faults or cancels; an already completed task returns an empty stream. | Async |
TakeUntilCancellation | TakeUntilCancellation<T>(CancellationToken cancellationToken) -> IOnityObservable<T> | Onity.Reactive | The synchronous form: disposes the upstream subscription when the token is canceled; an already canceled token returns Empty<T>(). | Sync |
Thread pool
Pure managed work only. Re-marshal with ObserveOnMainThread() before a subscriber that touches Unity objects.
| Operator | Signature | Namespace | Description | Kind |
|---|---|---|---|---|
ObserveOnThreadPool | ObserveOnThreadPool<T>() -> IOnityObservable<T> | Onity.Reactive | Re-posts each value onto one thread-pool worker in source order. | Thread |
SelectOnThreadPool | SelectOnThreadPool<TSource, TResult>(Func<TSource, TResult> selector, int maxConcurrency = 0) -> IOnityObservable<TResult> | Onity.Reactive | Runs selector on up to maxConcurrency workers; 0 means the processor count, negative throws. Results emit as workers finish; maxConcurrency: 1 keeps source order. | Thread |
SelectOnThreadPool (token) | SelectOnThreadPool<TSource, TResult>(Func<TSource, CancellationToken, TResult> selector, int maxConcurrency = 0) -> IOnityObservable<TResult> | Onity.Reactive | The same with a token that is canceled when the subscription is disposed. | Thread |
An exception thrown by selector or by a downstream observer on the worker goes to OnityObservableExceptionHandler; the stream continues.
Frame scheduling
| Operator | Signature | Namespace | Description | Kind |
|---|---|---|---|---|
ObserveOn | ObserveOn<T>(OnityFrameProvider frameProvider) -> IOnityObservable<T> | Onity.Reactive | Queues each value and delivers the queue on the provider’s next frame tick. Works with any OnityFrameProvider. | Frame |
ObserveOnMainThread | ObserveOnMainThread<T>() -> IOnityObservable<T> | Onity.Unity.Reactive | ObserveOn(OnityFrameProviders.Update). | Frame |
ObserveOnMainThread (phase) | ObserveOnMainThread<T>(OnityUnityFrameProvider frameProvider) -> IOnityObservable<T> | Onity.Unity.Reactive | ObserveOn with OnityFrameProviders.FixedUpdate or LateUpdate. | Frame |
Delay | Delay<T>(float delaySeconds, bool useUnscaledTime = false) -> IOnityObservable<T> | Onity.Unity.Reactive | Delays each value with an OnityCountdownTimer. 0 returns the source; negative throws. | Frame |
Task and stream bridges
| Member | Signature | Namespace | Description |
|---|---|---|---|
FirstAsync | FirstAsync<T>(CancellationToken cancellationToken = default) -> Task<T> | Onity.Reactive | Completes with the first value and unsubscribes. A canceled token completes the task as canceled. |
ToTask | ToTask(this IOnityObservable<Unit> source, CancellationToken cancellationToken = default) -> Task | Onity.Reactive | FirstAsync for a Unit stream. |
FirstOnityTask | FirstOnityTask<T>(CancellationToken cancellationToken = default) -> OnityTask<T> | Onity.Unity.Async | FirstAsync as an OnityTask<T>. |
ToOnityTask (Unit) | ToOnityTask(this IOnityObservable<Unit> source, CancellationToken cancellationToken = default) -> OnityTask | Onity.Unity.Async | ToTask as an OnityTask. |
ToOnityTask (last) | ToOnityTask<T>(CancellationToken cancellationToken = default) -> OnityTask<T> | Onity.Unity.Async | Completes with the last value when the source completes; a hot source that never completes needs the token. |
ToOnityTask (first or last) | ToOnityTask<T>(bool useFirstValue, CancellationToken cancellationToken = default) -> OnityTask<T> | Onity.Unity.Async | useFirstValue: true completes with the first value. A source that completes without a value faults the task with InvalidOperationException. |
ToObservable | ToObservable<T>(this OnityTask<T> task) -> IOnityObservable<T>; ToObservable(this OnityTask task) -> IOnityObservable<Unit> | Onity.Unity.Async | Exposes a task’s result (or one Unit) to every subscriber, including late ones. |
AsOnityAsyncEnumerable | AsOnityAsyncEnumerable<T>(int capacity) -> IOnityAsyncEnumerable<T> | Onity.Unity.Async | Buffers a subscription for pull-based consumption; a full buffer faults after the accepted values drain. |
AsObservable | AsObservable<T>(this IOnityAsyncEnumerable<T> source) -> IOnityObservable<T> | Onity.Unity.Async | Enumerates once per subscription and pushes the items. |
OperationCanceledException from these is normal cancellation; catch it where the flow starts.
Reactive property members
| Member | Signature | Namespace | Description |
|---|---|---|---|
| Constructor | new ReactiveProperty<T>(T initialValue = default, IEqualityComparer<T> comparer = null) | Onity.Reactive | The comparer decides which sets are changes; EqualityComparer<T>.Default when null. |
Value | T Value { get; set; } | Onity.Reactive | The setter calls SetValue. On IReadOnlyReactiveProperty<T> the property is read-only. |
SetValue | SetValue(T value) -> bool | Onity.Reactive | Publishes when the value differs under the comparer and returns true; an equal value returns false and publishes nothing. Throws ObjectDisposedException after Dispose(). |
Subscribe | Subscribe(Observer<T> observer, bool emitCurrentValue = true) -> IDisposable | Onity.Reactive | Also on IReadOnlyReactiveProperty<T>. Emits the current value first unless emitCurrentValue is false, then each change. |
Dispose | Dispose() -> void | Onity.Reactive | Disposes the inner subject; observers are not notified, and a pending WaitAsync is not completed. |
IReadOnlyReactiveProperty<T> declares only Value and the Subscribe above; it does not implement IOnityObservable<T>, so the operators need the ReactiveProperty<T> itself or a wrapper (new OnityObservable<T>(observer => property.Subscribe(observer))).
Reactive property waits
Pooled waits on any IReadOnlyReactiveProperty<T>; use them on the property’s thread (the main thread) and pass the scope token so a wait ends with its scope. See Await a ReactiveProperty with OnityTask for the rules.
| Member | Signature | Namespace | Description |
|---|---|---|---|
WaitAsync | WaitAsync<T>(this IReadOnlyReactiveProperty<T> property, CancellationToken cancellationToken = default) -> OnityTask<T> | Onity.Unity.Async | The next accepted change; the current value does not complete it. |
WaitUntilAsync | WaitUntilAsync<T>(this IReadOnlyReactiveProperty<T> property, Func<T, bool> predicate, CancellationToken cancellationToken = default) -> OnityTask<T> | Onity.Unity.Async | The first matching value; completes synchronously when the current value matches. A throwing predicate faults the task; a canceled token wins over a matching current value. |
AsLatestAsyncEnumerable | AsLatestAsyncEnumerable<T>(this IReadOnlyReactiveProperty<T> property, bool includeCurrent = true) -> IOnityAsyncEnumerable<T> | Onity.Unity.Async | A conflating stream: each move yields the latest value since the previous move or waits for the next change. Never ends on its own; the token cancels it. |
ToAsyncReactiveProperty | ToAsyncReactiveProperty<T>(this IReadOnlyReactiveProperty<T> property, CancellationToken cancellationToken) -> OnityReadOnlyAsyncReactiveProperty<T> | Onity.Unity.Async | Starts with the current value and follows the property until the token is canceled or it is disposed. |
BindTo | BindTo<T>(this IOnityReadOnlyAsyncReactiveProperty<T> source, ReactiveProperty<T> target, CancellationToken cancellationToken) -> IDisposable | Onity.Unity.Async | Writes the async property’s current and later values into target; dispose the result to stop. Main thread only. |
Factories and sources
| Member | Signature | Namespace | Description |
|---|---|---|---|
OnityObservable.FromEvent | FromEvent<T>(Action<Action<T>> addHandler, Action<Action<T>> removeHandler) -> IOnityObservable<T> | Onity.Reactive | Wraps a callback-style event pair; disposing the subscription removes the handler. |
OnityObservable.Return | Return<T>(T value) -> IOnityObservable<T> | Onity.Reactive | Emits value to each subscriber on subscribe. |
OnityObservable.Empty | Empty<T>() -> IOnityObservable<T> | Onity.Reactive | A shared instance that never emits. |
| Custom source | new OnityObservable<T>(Func<Observer<T>, IDisposable> subscribe) | Onity.Reactive | The function receives the observer callback and returns the disposable that ends the subscription (DisposableAction.Empty from Onity.Core when there is nothing to release). |
There are no Never, Create or Defer factories, and no Window, Zip, Switch, Concat, Publish, Share or RefCount on IOnityObservable<T>.
Subscribe overloads
Observer<T> is delegate void Observer<T>(T value), the callback every IOnityObservable<T> accepts. The extension overloads below are what you normally write.
| Overload | Signature | Namespace | Description |
|---|---|---|---|
Subscribe (value) | Subscribe<T>(Action<T> onNext) -> IDisposable | Onity.Reactive | On Subject<T>, ReactiveProperty<T> and the operator observables, stores onNext in the subscription node and invokes it directly, with no delegate wrapper; any other IOnityObservable<T> receives it wrapped in an Observer<T>. |
Subscribe (lifecycle) | Subscribe<T>(Action<T> onNext, Action<Exception> onError, Action<OnityResult> onCompleted) -> IDisposable | Onity.Reactive | Lifecycle callbacks. onCompleted runs with a success OnityResult when the subscription is disposed. onError is raised only by sources that report faults, such as OnityTask.ToObservable() and IOnityAsyncEnumerable<T>.AsObservable(); the core operators do not forward errors to it. |
Every IOnityObservable<T> also accepts a subclass of the public abstract OnityObserver<T> through Subscribe(OnityObserver<T>): override OnNextCore, optionally OnErrorCore, OnCompletedCore and OnDisposed, and the observer is stopped (IsStopped) after an error, a completion or Dispose.
Lifetime helpers
| Helper | Signature | Namespace | Disposes when |
|---|---|---|---|
AddTo (bag) | AddTo(this IDisposable disposable, CompositeDisposable compositeDisposable) -> IDisposable | Onity.Reactive | The bag is cleared or disposed. |
AddTo (scope) | AddTo<TDisposable>(this TDisposable disposable, IOnityScopeLifetime scope) -> TDisposable | Onity.DI | The DI scope ends; an ended scope disposes at once. See DI API. |
AddTo (component) | AddTo(this IDisposable disposable, Component owner) -> IDisposable | Onity.Unity.Reactive | The component is destroyed; the same as TakeUntilDestroy. |
TakeUntilDestroy | TakeUntilDestroy(this IDisposable disposable, Component owner) -> IDisposable | Onity.Unity.Reactive | The component is destroyed. |
TakeUntilDisable | TakeUntilDisable(this IDisposable disposable, Behaviour owner) -> IDisposable | Onity.Unity.Reactive | The behaviour is disabled, and on destroy. |
The helpers return the same disposable, so they chain after Subscribe. There is no AddTo(GameObject) overload.
Unity frame loops and timers
OnityUnityObservable (Onity.Unity.Reactive). The frame streams share one hidden pump component created on first use.
| Member | Signature | Namespace | Description |
|---|---|---|---|
EveryUpdate | EveryUpdate() -> IOnityObservable<Unit> | Onity.Unity.Reactive | Once per rendered frame. |
EveryFixedUpdate | EveryFixedUpdate() -> IOnityObservable<Unit> | Onity.Unity.Reactive | Once per physics step. |
EveryLateUpdate | EveryLateUpdate() -> IOnityObservable<Unit> | Onity.Unity.Reactive | Once per late update. |
Every* (token) | EveryUpdate(CancellationToken cancellationToken) and the Fixed / Late forms | Onity.Unity.Reactive | The stream ends when the token is canceled. |
Every* (thread mode) | EveryUpdate(OnityUnityThreadMode threadMode, int jobWorkItemCount = 64, int minCommandsPerJob = 32), also with a CancellationToken after threadMode, and the Fixed / Late forms | Onity.Unity.Reactive | Adds a job boundary around each emission: SingleThread, JobMultiThread, BurstJobMultiThread, DotsEventDriven. Observers still run on the main thread. Non-positive job parameters throw. |
Timer | Timer(float dueTimeSeconds, bool useUnscaledTime = false) -> IOnityObservable<Unit> | Onity.Unity.Reactive | One Unit after the delay; 0 emits on subscribe, negative throws. |
Interval | Interval(float intervalSeconds, bool useUnscaledTime = false) -> IOnityObservable<int> | Onity.Unity.Reactive | The cumulative tick count, starting at 1, every interval; non-positive throws. |
Providers
| Provider set | Namespace | Members |
|---|---|---|
OnityFrameProviders | Onity.Unity.Reactive | Update, FixedUpdate, LateUpdate: OnityUnityFrameProvider instances for ObserveOn. |
OnityTimeProviders | Onity.Unity.Reactive | UpdateScaled, UpdateUnscaled, UpdateRealtime, FixedScaled, FixedUnscaled, FixedRealtime, LateScaled, LateUnscaled, LateRealtime: OnityUnityTimeProvider instances for the time operators. Realtime uses Task.Delay; the others count the loop’s scaled or unscaled delta time. |
OnityTimeProvider.System | Onity.Reactive | The wall-clock default when no provider is passed. Subclass OnityTimeProvider (DelayAsync) for deterministic tests. |
Timers
OnityCountdownTimer(durationSeconds, useUnscaledTime, autoUpdate), OnityIntervalTimer(intervalSeconds, ...) and OnityStopwatchTimer(...) (Onity.Unity.Reactive) implement IOnityTimer: Start, Stop, Pause, Resume, Reset, Tick(deltaTime), IsRunning, IsFinished, CurrentTimeSeconds, Progress01, and the Started, Stopped, Completed events (IntervalElapsed(int) on the interval timer). With autoUpdate they advance from the shared Update stream.
Input System
Compiled with ENABLE_INPUT_SYSTEM.
| Member | Signature | Namespace | Description |
|---|---|---|---|
StartedAsObservable | StartedAsObservable(this InputAction action, CancellationToken cancellationToken = default) -> IOnityObservable<InputAction.CallbackContext> | Onity.Unity.Input | The action’s started callbacks; a cancelable token ends the stream. |
PerformedAsObservable | same shape | Onity.Unity.Input | The performed callbacks. |
CanceledAsObservable | same shape | Onity.Unity.Input | The canceled callbacks. |
OnityReactiveInputPlayer | new OnityReactiveInputPlayer(InputActionAsset actionAsset, float longPressDurationSeconds = 0.35f) | Onity.Unity.Input | Context stack (PushContext, PopContext, SetContext, ClearContexts) and GetButtonObservable, GetVector2Observable, GetFloatObservable, GetLongPressObservable, GetLongPressProgressObservable by action name. Long-press streams exist only on this type, not as InputAction extensions. |
Errors
| Situation | Behavior |
|---|---|
A subscriber throws during Subject<T>.OnNext or a ReactiveProperty<T> set | Caught and passed to OnityObservableExceptionHandler.Handler (a no-op until you assign one); the other subscribers are still notified. |
| A thread-pool selector or observer throws | Passed to OnityObservableExceptionHandler; the stream continues. |
A SelectAwait / WhereAwait delegate throws | The subscription ends; nothing is forwarded to the subscriber. |
A subscriber throws in a stream built from Observe<T>() | Propagates to the message Publish call; see Messaging API. |
ObjectDisposedException | OnNext, Subscribe or SetValue after Dispose(). |
ArgumentNullException / ArgumentOutOfRangeException | A null source or delegate; a negative count or a non-positive interval. |
OperationCanceledException | Normal cancellation of FirstAsync, ToTask, the OnityTask bridges and the property waits. |
OnityReactiveException | Declared for reactive-core failures; the shipped operators throw the standard exceptions above. |
Diagnostics
OnityObservableTracker (Onity.Reactive): EnableTracking, EnableStackTrace, GetSnapshot(List<OnityTrackedObservableInfo> results, bool includeDisposed = true), ClearDisposed(), ClearAll(). The Editor window Onity/Tools/Observable Tracker reads the same rows.
At a glance
Shipped: Where, Select, DistinctUntilChanged, Skip, SkipWhile, Take, TakeWhile, StartWith, Scan, Pairwise, Buffer (count and time), Merge, CombineLatest, Sample, Debounce, ThrottleLast, Throttle, SelectAwait, WhereAwait, TakeUntil (token and task), TakeUntilCancellation, ObserveOnThreadPool, SelectOnThreadPool, ObserveOn, ObserveOnMainThread, Delay, FirstAsync, ToTask, FirstOnityTask, ToOnityTask, ToObservable, AsOnityAsyncEnumerable, AsObservable, WaitAsync, WaitUntilAsync, AsLatestAsyncEnumerable, ToAsyncReactiveProperty, BindTo.
Not shipped: Window, Zip, Switch, Concat, Publish, Share, RefCount; the factories Never, Create, Defer. Pull-based async streams (IOnityAsyncEnumerable<T>) have Zip, Concat, Publish and Queue; see Stream operators. Model current state with ReactiveProperty<T> and notifications with messages (Messaging API).
The measured comparison with R3 and UniRx is in Reactive vs R3 and UniRx; the Reactive guide has the narrative and recipes.