Challenge 3: Defend Against a Bad readyState — Possible Solution ==================================================================== import { WebSocket } from "ws"; function broadcast(clients, message) { for (const client of clients) { if (client.readyState === WebSocket.OPEN) { client.send(message); } // any client NOT in the OPEN state (CONNECTING, CLOSING, or CLOSED) // is silently skipped rather than sent to } } // Example usage inside a "connection" handler: // // socket.on("message", (data) => { // broadcast(clients, data.toString()); // }); WHY THIS WORKS -------------- - WebSocket.OPEN is a constant (numerically 1) exposed on the WebSocket class itself — comparing client.readyState against it is the standard, readable way to check state, rather than hardcoding the number 1 directly. - The four possible readyState values are CONNECTING (0), OPEN (1), CLOSING (2), and CLOSED (3). Only OPEN is safe to send() to: - CONNECTING means the handshake from Chapter 2 hasn't finished yet — sending now would throw, since there's no established connection to send frames over. - CLOSING and CLOSED mean the connection is going away or already gone — sending would either throw or silently go nowhere. - This check matters specifically in a broadcast loop because of TIMING: the "clients" Set may still contain a socket that started closing a moment ago but whose "close" event (and the clients.delete() cleanup from Challenge 2) hasn't fired yet by the time this loop runs. Without the readyState check, that one stale entry could throw an error and, depending on how the loop is written, potentially stop the broadcast from reaching the remaining, still-healthy clients. - Wrapping this in a reusable broadcast(clients, message) function keeps the safety check in exactly one place, so every part of the application that needs to send to multiple clients automatically gets the same protection — rather than every call site needing to remember to repeat the readyState check by hand.