A MessageChannel creates two linked ports. A host can transfer one port to a frame, leaving subsequent traffic on that connection rather than on a global window message listener. The port does not make an untrusted frame safe: the initial transfer still needs an exact target origin, and every application message still needs schema and authority checks. Its benefit is ownership of a conversation. Give each channel a task ID, a state machine, a timeout, and an explicit close rule.
MessageChannel Lifetime and Request Correlation
Working case
Reviewer 47 asks a document viewer for page 29 of a private attachment. The host creates a port pair and transfers one end after the viewer declares readiness. The viewer sends a page-ready response, then small progress updates while preparing accessible text. If the reviewer closes the attachment or switches cases, the host closes its port and cancels the server request. A late progress message is ignored. Another viewer instance gets a new channel; it cannot inherit the old task's authority simply by occupying the same frame element.
Implementation boundary
function applyViewerProgress(currentTask, incoming) {
if (currentTask.state !== 'active' || incoming.taskId !== currentTask.id) return currentTask;
return { ...currentTask, completedPages: Math.min(incoming.completedPages, currentTask.totalPages) };
}
console.log(applyViewerProgress({ id: 30, state: 'active', completedPages: 2, totalPages: 47 }, { taskId: 29, completedPages: 47 }).completedPages);
// Output: 2Keep the channel in an owner object tied to the currently mounted viewer and attachment reference. Establish the first handshake over window messaging with origin and source checks, then transfer a port with an exact target origin. Define states such as waiting, active, completed, canceled, and expired. Each message carries a task ID, version, type, and bounded payload. Close both available endpoints during teardown and remove handlers and timers. A port can outlive a frame's visible DOM if references remain, so navigation and account switch must trigger cleanup. Server requests still verify the current user and attachment; a port is not a bearer credential.
Cost and boundaries
A channel reduces unrelated global-message traffic and makes cleanup observable, but each live task holds ports, handlers, buffers, and possibly workers. Set a small maximum for concurrent tasks and progress frequency. A progress event for every decoded line can overwhelm the main thread; coalesce updates at a useful cadence. A correlation map lookup is O(1) on average, while retained messages and unfinished promises grow with unresolved tasks. Track active channels, timeouts, and cancellation completion, then test the limits on slow devices.
Failure trace
The viewer reloads but the host keeps the old port and marks the new viewer ready because it reuses the same iframe node. Page-ready from task 29 arrives after task 30 has started, and the host shows the wrong attachment. Force a reload during transfer, close the report before the first response, and switch accounts while progress is pending. In each case, the old channel must close and no old payload may appear. The new task must request a fresh port and fresh server authorization.
Verification
- Port count returns to baseline after viewer teardown.
- Late task messages do not change the current attachment.
- A failed channel leaves the preview path usable.
Practice drill
Model the channel owner as an object with task ID and state. Start task 29, cancel it, and then deliver its completion message. The state reducer must leave the current task unchanged. Start task 30 and count the active ports before and after teardown. Add a timeout and a manual retry control. Confirm that a failed transfer does not hide the attachment's ordinary preview link.
Decision note
Use ports for a scoped conversation, not as a substitute for message validation or server permission.
Common Mistakes
- Leaving transferred ports alive after a route change.
- Treating the port object as proof of user authorization.
- Sending unbounded progress events for every decoded unit.
Connected lessons
Build Project: embedded attachment viewer contract and review Web Development: embedded interfaces and build integrity quiz; follow Embedded Interfaces and Cross-Window Contracts; Frame Sandbox, Capabilities, and Fallback; Cross-Window Messages: Origin, Source, Schema, and Replay; Popup Handoff, Return, and Window Ownership; Workers, Messages, Transfer, and Cancellation; Stream Cancellation and Partial-Result Contract.
