Rask
Rask
v0.23.1-alpha.0.496
You're reading the Rask v0.23.1-alpha.0.496 guides. Updated .View source
All guides

IBackgroundSync

Ask the browser to wake the app when connectivity returns, or on a recurring schedule.

  • Wraps: Background Synchronization API + Web Periodic Background Synchronization API
  • MDN: SyncManager · PeriodicSyncManager
  • Home: Rask.Wasm.Browser (WASM only)
  • Shape: one-shot (register / list / unregister) + subscription (OnSync pushes to a callback)
  • Availability: Web/Server ⬜ · PWA/WASM ✅

Both registrations live on the service-worker registration, and a Server app renders over a live WebSocket with no client-side runtime to wake into — so this is WASM-only, and it needs a registered service worker (every Rask WASM PWA has one; see the PWA guide).

Read this before you rely on it

The browser fires the sync even with the tab closed. Rask's guarantee is narrower, and the gap is the part worth designing around: the .NET runtime lives in the page, not in the service worker, so your C# only runs while a client is open. Rask's service worker forwards the woken-up tag to every open client; with none open the registration is consumed without your handler seeing it.

Two consequences:

  • Re-request your tags at boot. Treat a registration as best-effort rather than durable queue state. Keep the work itself in IIndexedDb or OPFS (Navigator.Storage.GetDirectory() in Rask.Web) and let the sync be the nudge to drain it, not the store.
  • The realistic win is a backgrounded tab, not a closed one. A hidden or frozen tab is still a client, so it wakes and drains the moment the network is back — which is the case most offline-first apps actually hit.

Support is Chromium-only at the time of writing. Every call degrades to "unavailable" (false, or an empty list) rather than throwing, so a feature check is optional and a fallback is not.

Using it

public sealed class DraftQueue(IBackgroundSync sync) : Component, IAsyncDisposable
{
    private IAsyncDisposable? _subscription;

    public override async Task OnFirstRender()
    {
        // Subscribe BEFORE requesting: a sync that landed while the page was still booting is held for
        // the first subscriber, so an event that beat your startup code still reaches it.
        _subscription = await sync.OnSync(async e =>
        {
            if (e.Tag == "flush-drafts") await Flush();
            StateHasChanged();
        });

        // Best-effort, and re-requested every boot — the browser may have consumed the last one while
        // no tab was open.
        await sync.RequestSync("flush-drafts");
    }

    public async ValueTask DisposeAsync() => await (_subscription?.DisposeAsync() ?? ValueTask.CompletedTask);
}

Periodic sync is gated on a permission the browser grants on its own terms (Chromium ties it to the app being installed and to site engagement). There is no API to request it, so check, don't ask:

if (await sync.IsPeriodicSupported() && await sync.GetPeriodicPermission() == "granted")
{
    // A floor, not a schedule — the browser decides the real cadence and in practice fires far less
    // often than you ask.
    await sync.RequestPeriodicSync("refresh-feed", 12.Hours);
}

OnSync delivers both kinds; check BackgroundSyncEvent.Periodic to tell them apart. It is a subscription handler, not a chain-set callback, so calling StateHasChanged() in it is correct and RASK026 does not apply.

See also

Confirm