Сборка живого чата на клиенте

Складываем события и send() в полноценный клиент чата (концептуально).

Клиент реалтайма — код в браузере, который держит WebSocket, шлёт действия пользователя серверу и отображает входящие события.

Скелет клиента

Соберём типичную обвязку: подключение, отправка по нажатию кнопки, отрисовка входящих сообщений. Код требует живого сервера, поэтому он для чтения:

// Браузер: клиент чата
const socket = new WebSocket("wss://example.com/chat");

socket.onopen = () => {
  setStatus("онлайн");
};

socket.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === "chat") {
    addLine(msg.user + ": " + msg.text);
  }
};

socket.onclose = () => {
  setStatus("оффлайн");
};

// при нажатии кнопки «Отправить»
function sendChat(text) {
  if (socket.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify({ type: "chat", text: text }));
  }
}

Поток данных

[поле ввода] --клик «Отправить»--> sendChat()
                                      |
                                send() ---------> СЕРВЕР
                                                    |
                          (сервер раздаёт всем) <----+
                                      |
            onmessage <--- фрейм с чужим сообщением
                                      |
                                  addLine()  --> [лента чата]

Защита от отправки в закрытый канал

Перед send() всегда проверяем readyState: если канал не OPEN (идёт реконнект, обрыв), отправка либо бросит ошибку, либо потеряется. Хороший клиент в таком случае ставит сообщение в очередь и шлёт после восстановления.

Как работает под капотом

Важно понять асимметрию: ваше сообщение и чужие приходят разными путями. Когда вы жмёте «Отправить», вы не дорисовываете строку сразу — вы шлёте её серверу. Сервер раздаёт её всем участникам, включая вас, и только тогда сработает onmessage и строка появится в ленте. Так все видят одинаковый порядок сообщений, заданный сервером, а не локальной гонкой.

Частые ошибки

  • Рисовать своё сообщение локально до ответа сервера. Тогда порядок у разных людей разойдётся; либо ждите эхо от сервера, либо делайте «оптимистичный» рендер с последующей синхронизацией.
  • Не обрабатывать неизвестные типы сообщений. Сервер может прислать type, которого клиент не знает; игнорируйте такие, а не падайте.
  • Забыть статус соединения. Пользователь должен видеть «онлайн/оффлайн», иначе непонятно, доходят ли его сообщения.

Итоги

  • Клиент держит сокет, шлёт действия через send() и рисует входящие в onmessage.
  • Перед отправкой проверяйте readyState === OPEN.
  • Ваше сообщение возвращается эхом от сервера — так гарантируется единый порядок для всех.
  • Показывайте статус соединения и игнорируйте неизвестные типы сообщений.
Проверьте себя
1. Почему сообщение пользователя лучше отображать после эха от сервера?
AТак быстрее
BЧтобы у всех участников был единый порядок сообщений, заданный сервером
CИначе браузер упадёт
DЧтобы экономить трафик
2. Что стоит проверить перед каждым send()?
AДлину сообщения
BЧто readyState === OPEN
CВерсию браузера
DНаличие интернета через ping HTTP