self.defer() — post-render scheduling

Phoenix send(self(), :foo) parity. Schedule a callback to run AFTER the current render+patch flushes to the client. Same WS message cycle, no second re-render. Useful for analytics, audit logs, telemetry.

Click counter

self.defer()

Click the button. The counter updates immediately on render. The deferred audit-log entry is written AFTER the patch flushes — you'll see it on the next interaction (audit log re-renders then).

0

Audit log is empty. Click the button above.

The whole pattern

from djust import LiveView

class DeferDemoView(LiveView):
    @event_handler()
    def increment(self, **kwargs):
        # Re-render fires immediately with the new count
        self.click_count += 1

        # Defer fires AFTER the render+patch is flushed
        # to the client. Same WS message cycle, no second render.
        self.defer(self._record_metric, 'click', self.click_count)

    def _record_metric(self, event_name, count):
        # In production: post to analytics endpoint, write audit row, etc.
        # For the demo we log to a server-side list that re-appears
        # on the next render (next user interaction).
        self.audit_log.append({
            'event': event_name, 'count': count, 'ts': time.time()
        })

defer vs alternatives:

  • self.defer(cb, *a) — sync, after-render, no re-render. Best for fire-and-forget side effects.
  • self.start_async(cb, *a) — background thread, re-render on completion.
  • self.assign_async("name", loader) — background, re-render with self.name = AsyncResult(...). Best for async data loading.
  • @background decorator — handler itself runs in background. Re-renders on completion.