ข้ามไปยังเนื้อหา

Events and readyState

WebSocket เป็นออบเจกต์ที่ขับเคลื่อนด้วย event code ของคุณไม่ต้องคอย poll หรือบล็อกรอ แต่คุณผูก handler ไว้แล้วปล่อยให้การเชื่อมต่อเรียกกลับมาหาคุณเมื่อมีอะไรเกิดขึ้น มี event อยู่ สี่ ตัวพอดี และฟิลด์เดียว — readyState — ที่บอกเสมอว่าคุณอยู่ตรงไหน เชี่ยวชาญสิ่งเหล่านี้แล้ว API จะไม่มีอะไรเซอร์ไพรส์อีก

WebSocket บนเบราว์เซอร์ทุกตัวยิง event สี่ตัวเดียวกัน ไม่มากกว่านี้:

  • open — handshake สำเร็จ การเชื่อมต่อตอนนี้เป็น OPEN และพร้อมใช้งานแล้ว นี่คือที่เดียวที่ปลอดภัยในการเริ่มส่ง
  • message — มีข้อความมาถึงจาก peer payload อยู่บน event.data
  • close — การเชื่อมต่อปิดลงแล้ว event นี้พกตัวเลข event.code และสตริง event.reason ที่อธิบายว่าทำไม
  • error — มีบางอย่างผิดพลาด เป็นแค่ข้อมูลบอกกล่าวและจงใจให้คลุมเครือ (เพื่อความปลอดภัย) event close มักจะตามมาเสมอ

คุณผูก handler ได้สองวิธี วิธีที่เร็วที่สุดคือผ่าน property on*:

const ws = new WebSocket('wss://example.com');
ws.onopen = () => console.log('open');
ws.onmessage = (event) => console.log('data:', event.data);
ws.onerror = () => console.log('error');
ws.onclose = (event) => console.log('closed', event.code, event.reason);

หรือใช้ addEventListener ซึ่งช่วยให้คุณผูก handler ได้มากกว่าหนึ่งตัวต่อหนึ่ง event:

ws.addEventListener('message', (event) => console.log(event.data));

ขณะที่ event บอกคุณว่าสิ่งต่าง ๆ เปลี่ยน เมื่อไหร่ readyState บอกคุณว่าสถานะปัจจุบันคืออะไร ในทุกขณะ เป็นตัวเลข และคลาสเปิดให้เข้าถึงค่าคงที่ที่มีชื่อสำหรับแต่ละค่า:

WebSocket.CONNECTING; // 0 — handshake in progress
WebSocket.OPEN; // 1 — ready to send and receive
WebSocket.CLOSING; // 2 — close handshake underway
WebSocket.CLOSED; // 3 — finished, or never opened
if (ws.readyState === WebSocket.OPEN) {
ws.send('safe to send now');
}

สองมุมมองนี้สอดคล้องกันอย่างลงตัว: event open ยิงตอนที่คุณเข้าสู่สถานะ 1 และ event close ยิงตอนที่คุณเข้าสู่สถานะ 3 การตรวจ ws.readyState === WebSocket.OPEN ก่อนส่งคือ guard มาตรฐานที่กันบั๊กแบบ “ฉันพยายามส่งเร็วเกินไปหรือช้าเกินไป”

นี่คือวงจรชีวิตเดียวกันจากบทแรก ตอนนี้กำกับด้วย event ที่ยิงในแต่ละการเปลี่ยนสถานะ:

stateDiagram-v2
  [*] --> CONNECTING: new WebSocket()
  CONNECTING --> OPEN: open event
  CONNECTING --> CLOSED: error then close event
  OPEN --> CLOSING: close() / peer closes
  CLOSING --> CLOSED: close event
  OPEN --> CLOSED: drop, then close event
  note right of OPEN: message events may fire repeatedly here
  CLOSED --> [*]
สถานะอยู่บน node, event อยู่บนการเปลี่ยนผ่าน

event message พิเศษตรงที่: ไม่ใช่การเปลี่ยนสถานะ event นี้ยิงได้กี่ครั้งก็ได้ขณะที่คุณอยู่ในสถานะ OPEN ซึ่งนั่นแหละคือจุดประสงค์ทั้งหมดของการเชื่อมต่อแบบคงอยู่ยาวนาน

เดโมใช้ echo socket ในหน้าเว็บที่มี WebSocket API จริง จึงรันได้ทุกที่โดยไม่ต้องมี server เดโมล็อก readyState ในทุกขั้นตอนโดยใช้ค่าคงที่ที่มีชื่อ ในแอปจริงมีเพียงบรรทัดแรก — new WebSocket('wss://your-server') — เท่านั้นที่ต่างไป

JavaScript

ลองตามรอยผลลัพธ์เทียบกับไดอะแกรม: CONNECTING ตอนสร้าง, OPEN ใน handler ของ open, ยังคง OPEN เมื่อ message มาถึง, CLOSING ในวินาทีที่เรียก close() และ CLOSED เมื่อ event close ยิง — พกค่า code 1000 และ reason ที่เราส่งไปมาด้วย

readyStateค่าความหมาย
CONNECTING0กำลัง handshake
OPEN1เชื่อมต่อแล้ว พร้อมส่ง/รับ
CLOSING2กำลังปิด connection
CLOSED3ปิดแล้ว

ส่ง Message เมื่อ readyState ไม่ใช่ OPEN อาการ:

  • ws.send(data) ใน event handler โดยไม่ check state
  • CLOSING state ทำให้ throw error
  • check ก่อนส่งเสมอ: if (ws.readyState === WebSocket.OPEN) ws.send(data)

ไม่แยก onerror กับ onclose อาการ:

  • handle แค่ onclose — ไม่รู้ว่า close เพราะ error หรือ normal
  • onerror เกิดก่อน onclose เสมอเมื่อมี error
  • log ใน onerror เพื่อรู้สาเหตุ, reconnect ใน onclose

💡 ตัวอย่างจากของจริง

GitHub Codespaces Terminal:

  • ตรวจ readyState ก่อนส่ง input ทุก keystroke
  • CONNECTING state → queue keystroke ไว้ส่งเมื่อ OPEN

Figma:

  • แสดง “Reconnecting…” UI เมื่อ readyState เปลี่ยนจาก OPEN เป็น CLOSED
  • user รู้ว่า connection ขาดและกำลัง reconnect
event ใดสามารถยิงได้หลายครั้งในระหว่างการเชื่อมต่อครั้งเดียว?
WebSocket.OPEN ตรงกับค่าตัวเลขใด?
คุณจะรู้ได้จากที่ไหนว่าทำไมการเชื่อมต่อจึงจบลง?