deadline_future 0.1.0 copy "deadline_future: ^0.1.0" to clipboard
deadline_future: ^0.1.0 copied to clipboard

A Dart package that provides graceful timeout handling by returning cached or fallback values instead of throwing exceptions, ensuring resilient real-time applications and uninterrupted user experiences.

deadline_future 🏁 #

pub.flutter-io.cn Dart SDK License: BSD-3 style: lints

A time-bounded Future that never throws TimeoutException.
Returns the freshest data available β€” live, cached, or a static fallback β€” instead of crashing.


The Problem #

// ❌ Dart's built-in Future.timeout β€” "all or nothing"
try {
  final price = await fetchPrice().timeout(const Duration(seconds: 2));
} on TimeoutException {
  // The result was discarded even if it arrived 1ms later.
  // You must handle the exception every single time.
}

In real-time apps this is painful: every slow network spike crashes the UI, every late response is wasted, and you have no visibility into why the fallback was used.


The Solution #

// βœ… deadline_future β€” three-tier graceful fallback
final result = await fetchPrice().withDeadline(
  const Duration(seconds: 2),
  fallback: lastKnownPrice,      // πŸ›‘οΈ tier 3: static safety net
  cacheKey: 'btc_price',         // πŸ’Ύ tier 2: automatic smart cache
  cacheTtl: const Duration(minutes: 5),
  onTimeout: () => showSpinner(), // called the moment deadline hits
  context: 'BTC price widget',   // appears in logs & exceptions
);

// result is ALWAYS available β€” never null, never an exception
switch (result.source) {
  case DeadlineResultSource.completed:
    print('βœ… Live  β€” ${result.actualDuration!.inMilliseconds}ms');
  case DeadlineResultSource.cached:
    print('πŸ’Ύ Cached β€” showing last known value');
  case DeadlineResultSource.fallback:
    print('πŸ›‘οΈ  Fallback β€” network is struggling');
}

if (result.isDegraded) showStaleBadge(); // one-liner UI indicator
updatePrice(result.value);               // always works

Resolution Strategy #

withDeadline(deadline, fallback: F, cacheKey: K)
          β”‚
          β”œβ”€ Future completes in time?      β†’ βœ… live result
          β”‚                                    (stored in cache for next time)
          β”‚
          β”œβ”€ Timeout + cache[K] valid?      β†’ πŸ’Ύ cached result
          β”‚
          β”œβ”€ Timeout + F != null?           β†’ πŸ›‘οΈ  fallback result
          β”‚
          └─ Timeout + nothing available?   β†’ πŸ”΄ DeadlineExceededException

Self-healing cache: even after a timeout, the original Future keeps running. When it finally completes, its value is stored in the cache β€” automatically improving the next call.


Installation #

dependencies:
  deadline_future: ^0.1.0
dart pub get

Quick-start Recipes #

Minimal β€” static fallback only #

final result = await fetchUserProfile().withDeadline(
  const Duration(seconds: 2),
  fallback: UserProfile.guest(),
);
print(result.value.displayName); // always available

Smart cache β€” best for repeated calls #

// First call: Future wins β†’ cached.
await fetchBtcPrice().withDeadline(
  const Duration(seconds: 2),
  cacheKey: 'btc',
  cacheTtl: const Duration(minutes: 5),
);

// Second call: network degraded β†’ served from cache.
final r = await fetchBtcPrice().withDeadline(
  const Duration(milliseconds: 300),
  cacheKey: 'btc',
  fallback: 0.0,
);

Duration shorthand #

// Clean, readable deadlines:
await fetch().withDeadline(3.seconds);
await fetch().withDeadline(500.milliseconds);
await fetch().withDeadline(2.minutes);

Batch concurrent calls #

final results = await [fetchBtc(), fetchEth(), fetchSol()]
    .withDeadlineAll(
      const Duration(milliseconds: 500),
      cacheKeys: ['btc', 'eth', 'sol'],
      fallback: 0.0,
      onTimeout: (i) => print('Feed $i timed out'),
    );

Exception handling #

try {
  await myFuture.withDeadline(const Duration(seconds: 1));
} on DeadlineExceededException catch (e) {
  // Only thrown when NO cache entry AND NO fallback exist.
  print('Exceeded ${e.deadline.inMilliseconds}ms β€” ${e.context}');
} on InvalidDeadlineDurationException {
  // Synchronous guard against Duration.zero / negative values.
}

Global configuration (app startup) #

void main() {
  DeadlineConfig.enableGlobalCache = true;
  DeadlineConfig.defaultCacheTtl   = const Duration(minutes: 10);
  DeadlineConfig.maxCacheEntries   = 500;
  DeadlineConfig.logLevel          = kDebugMode
      ? DeadlineLogLevel.info
      : DeadlineLogLevel.silent;
  runApp(const MyApp());
}

API Reference #

Future<T>.withDeadline() #

Parameter Type Required Description
deadline Duration βœ… Max wait time. Must be positive.
fallback T? Static value returned on timeout (if cache miss).
cacheKey String? Enables smart cache. Unique per call site.
cacheTtl Duration? Per-call TTL. Overrides defaultCacheTtl.
onTimeout void Function()? Called the instant the deadline elapses.
context String? Label for logs and exception messages.

Returns: Future<DeadlineResult<T>>


DeadlineResult<T> #

Member Type Description
value T The resolved value.
isTimedOut bool Did the deadline elapse?
source DeadlineResultSource completed, cached, or fallback.
isLive bool Shorthand: source == completed.
isDegraded bool Shorthand: !isLive.
isFromCache bool Shorthand: source == cached.
isFromFallback bool Shorthand: source == fallback.
actualDuration Duration? How long the original Future took.
resolvedAt DateTime UTC timestamp of resolution.
copyWith(...) DeadlineResult<T> Non-destructive field override.

DeadlineConfig (static) #

Property / Method Default Description
enableGlobalCache true Master cache toggle.
defaultCacheTtl null Default TTL for all cache entries.
maxCacheEntries 200 Cache capacity before FIFO eviction.
ignoreErrorsAfterDeadline true Swallow late Future errors.
logLevel silent Controls stdout diagnostic output.
reset() β€” Restores defaults + clears cache.
clearCache() β€” Empties the cache only.
evictCacheEntry(key) β€” Removes one entry by key.
cacheSize β€” Current number of live cache entries.

Comparison Table #

Feature Future.timeout() withDeadline()
Future completes in time βœ… Value βœ… Value + metadata
Timeout with handler βœ… onTimeout value βœ… Fallback / cache
Timeout without handler ❌ TimeoutException πŸ”Ά DeadlineExceededException*
Late result πŸ—‘οΈ Discarded πŸ’Ύ Cached for next call
Next call after timeout ❌ Crashes again βœ… Served from cache
Result metadata ❌ None βœ… DeadlineResultSource
onTimeout callback ❌ βœ…
Global config ❌ βœ… DeadlineConfig
Batch API ❌ βœ… withDeadlineAll
Duration shorthand ❌ βœ… 3.seconds

* Only thrown as a last resort β€” cache and fallback are checked first.


Ideal Use Cases #

  • πŸ“ˆ Crypto / stock price feeds β€” show last known price while refreshing
  • πŸ’¬ Chat heads β€” display cached messages while server is slow
  • πŸ“Š Live dashboards β€” partial data is better than blank panels
  • 🏟️ Sports scores β€” stale score with "updating..." badge
  • πŸ”„ Retry wrappers β€” compose with withDeadline for per-attempt limits
  • 🌐 Any API call where "stale but available" beats "fresh but crashed"

Testing #

dart test

Run the examples:

dart run example/main.dart

Run the benchmarks:

dart run benchmark/throughput_bench.dart

License #

BSD-3-Clause Β© 2026 deadline_future contributors

0
likes
140
points
25
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Dart package that provides graceful timeout handling by returning cached or fallback values instead of throwing exceptions, ensuring resilient real-time applications and uninterrupted user experiences.

Repository (GitHub)
View/report issues

Topics

#async #future #timeout #real-time #utility

License

BSD-3-Clause (license)

Dependencies

meta

More

Packages that depend on deadline_future