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

Coalesces per-item value lookups into batched requests, with debouncing, per-id caching, retry with backoff, and typed per-id state.

example/main.dart

// An example is a script: printing is how it shows its work.
// ignore_for_file: avoid_print

import 'package:batch_fetcher/batch_fetcher.dart';

/// A list of 120 inventory events is on screen. Each row shows a cost that only
/// the server can compute, and the endpoint takes at most 50 ids per call.
///
/// Run with: dart run example/main.dart
Future<void> main() async {
  final api = _FakeCostsApi();

  final fetcher = BatchFetcher<String, double, BatchKey>(
    // The only thing the fetcher needs to know about your domain.
    request: (ids, key) async => BatchOutcome.resolved(
      await api.costs(ids, locationId: key.toString()),
    ),
    // A cost of 0 means the server is still aggregating; give it two more
    // looks, three seconds apart, then believe it.
    settle: const SettleWhen<double>(_isPriced, maxAttempts: 2),
    observer: _PrintObserver(),
  );

  final visibleIds = [for (var i = 0; i < 120; i++) 'event_$i'];
  final scope = BatchScope<String, BatchKey>(
    key: BatchKey(const ['loc_dubai_marina']),
    ids: visibleIds,
  );

  // Safe to call on every rebuild: cached, in-flight and backing-off ids are
  // all skipped.
  await fetcher.fetch(scope);
  await fetcher.fetch(scope);

  print('\nrequests sent: ${api.callCount} for ${visibleIds.length} ids');
  print('event_0  -> ${fetcher.entryOf('event_0')}');
  print('event_7  -> ${fetcher.entryOf('event_7')}');
  print('resolved -> ${fetcher.values.length}');

  await fetcher.dispose();
}

bool _isPriced(double cost) => cost != 0;

class _PrintObserver extends BatchFetcherObserver<String> {
  @override
  void onBatchStart(Object? key, List<String> ids) =>
      print('-> request for ${ids.length} ids under $key');

  @override
  void onRetryScheduled(String id, int attempt, Duration delay) =>
      print('   retry $id (attempt $attempt) in ${delay.inMilliseconds}ms');
}

/// Fails id 7 once, then resolves everything.
class _FakeCostsApi {
  int callCount = 0;

  Future<Map<String, double>> costs(
    List<String> ids, {
    required String locationId,
  }) async {
    callCount++;
    await Future<void>.delayed(const Duration(milliseconds: 10));
    if (callCount == 1 && ids.contains('event_7')) {
      throw StateError('transient upstream failure');
    }
    return <String, double>{
      for (final id in ids) id: 10.0 + ids.indexOf(id),
    };
  }
}
0
likes
160
points
105
downloads

Documentation

API reference

Publisher

verified publishersupy.io

Weekly Downloads

Coalesces per-item value lookups into batched requests, with debouncing, per-id caching, retry with backoff, and typed per-id state.

Repository (GitHub)
View/report issues

Topics

#batching #debounce #cache #retry #fetch

License

MIT (license)

Dependencies

collection, meta

More

Packages that depend on batch_fetcher