# Timeout Pattern Guide The Timeout pattern aborts an operation that takes too long, so a hung dependency cannot hang your application. ## When to Use - Calls to network services that may hang indefinitely - Third-party APIs without their own reliable timeout - Enforcing an SLA on how long an operation is allowed to take - Protecting the UI thread from a slow background call ## When NOT to Use - Operations that already have a robust timeout of their own (e.g. an HTTP client with a configured timeout lower than yours) - Non-idempotent operations that cannot be safely abandoned mid-flight (the underlying task keeps running in the background even after the timeout fires) - As a substitute for cancellation support in the called API - Timeout abandons the wait, it does not stop the work ## Quick Start ```pascal uses Murphy.Policy.Timeout; var Policy: ITimeoutPolicy; begin Policy := TTimeoutBuilder .Handle([]) .After(TTimeSpan.FromSeconds(5)) .Build; Policy.Execute(procedure begin CallSlowService; end); end; ``` ## Common Scenarios ### Enforcing a Hard Deadline ```pascal procedure FetchWithDeadline; var Policy: ITimeoutPolicy; begin Policy := TTimeoutBuilder .Handle([]) .After(TTimeSpan.FromSeconds(3)) .Build; Policy.Execute( procedure begin HTTP.Get('https://api.example.com/data'); end); end; ``` ### Logging on Timeout ```pascal var Policy: ITimeoutPolicy; begin Policy := TTimeoutBuilder .Handle([]) .After(TTimeSpan.FromSeconds(5)) .OnTimeout(procedure(Context: TTimeoutContext) begin WriteLn(Format('Timed out after %s (limit was %s)', [Context.ElapsedTime.ToString, Context.TimeoutDuration.ToString])); end) .Build; Policy.Execute(procedure begin CallSlowService; end); end; ``` ### Handling the Timeout Exception ```pascal var Policy: ITimeoutPolicy; begin Policy := TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(2)).Build; try Policy.Execute(procedure begin CallSlowService; end); except on E: ETimeoutRejectedException do WriteLn('Operation aborted: took too long'); end; end; ``` ### Combined with Retry Retry the operation with a fresh timeout budget on each attempt: ```pascal uses Murphy.Policy.Retry, Murphy.Policy.Timeout, Murphy.Policy.Wrap; var Policy: IPolicyWrap; begin Policy := TPolicyWrapBuilder.Wrap([ TRetryBuilder.Handle(ETimeoutRejectedException).Retry(3), TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(2)) ]); Policy.Execute(procedure begin CallSlowService; end); end; ``` ## Best Practices 1. **Pick a duration tied to a real requirement** - a UI budget, an SLA, or an upstream deadline 2. **Remember losing work keeps running** - the abandoned task is not cancelled, only its result is ignored; only use Timeout on operations safe to abandon 3. **Use `OnTimeout` for observability** - log elapsed time to tune the threshold over time 4. **Combine with Retry** - retry a timed-out attempt rather than giving up immediately 5. **Note that `Handle()` is ignored** - the timeout fires regardless of exception type; any exception types passed are not used by this pattern ## See Also - [Timeout API Reference](../API-Reference/Timeout-Policy.md) - [Combining Patterns](Combining-Patterns.md) --- [Back to Index](../Home.md)