Parent window
Connecting…

Async return values

BroadcastChannelwindowId
The return-value pattern
Same return-value pattern as the postMessage demo, but the popup opens with noopener — it runs on its own event loop, so a long-running task in the parent never freezes the dialog. The real advantage of this transport is this thread isolation, not the call shape itself.
Last result
Whatever the child popup most recently returned.

// no result yet

Code
Both sides of the pattern. The parent awaits a typed value; the child turns user interaction into a procedure return.
parent.tsx
// Parent: open the popup and await a typed value.
// The BroadcastChannel transport opens the child with `noopener`, so the
// child runs on its OWN event loop — a busy parent does not freeze the
// dialog and vice versa.
const child = await iwpc.open(`./child3?kind=color`, {
  width: 520,
  height: 540
});

const hex = await child.invoke<void, string | null>(
  'PICK_COLOR',
  undefined,
  { timeout: 5 * 60 * 1000 } // generous, the user is in the loop
);

if (hex === null) {
  // user cancelled
} else {
  setColor(hex);
}
child.tsx
// Child: turn a user interaction into a procedure return value.
const resolverRef = useRef<((v: string | null) => void) | null>(null);

useEffect(() => {
  iwpc?.register('PICK_COLOR', () => {
    return new Promise<string | null>((resolve) => {
      resolverRef.current = resolve;
    });
  });
  return () => iwpc?.unregister('PICK_COLOR');
}, [iwpc]);

const onPick = (hex: string) => {
  resolverRef.current?.(hex);
  resolverRef.current = null;
  // Let the RETURN message flush over BroadcastChannel, then tear down.
  setTimeout(() => iwpc?.close(), 120);
};