9 min read

Eye Training · Events, Listeners and Sockets

Every on asks four questions: when is it removed, how many times is it added, what if it fires twice at once, and what happens across a reconnect?

Part 2 · Surface 14. Bug counts are hidden and some snippets are clean. See Start Here for how to use these drills.

The snippets use the browser WebSocket, Node's EventEmitter, and Socket.IO (socket.on, socket.emit, io.to(room)). The habits are the same for all three.


J · The basics

Bug hunt
J · Open a chat room
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
J · Watch prices
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
J · Keyboard controls
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
J · Subscribe to live orders
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
J · Shortcuts with cleanup
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min

M · Three years in, these should jump out

Bug hunt
M · Render the cart
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Uploader
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min
Bug hunt
M · Reconnect
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Socket.IO subscriptions
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Who's online
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min
Bug hunt
M · Live stats
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Wait for ready
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min

S · Where seniors stand out

Bug hunt
S · Balance updates
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min
Bug hunt
S · Shared cursors
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
S · Resume after reconnect
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min
Bug hunt
S · Resume, done right
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
S · Team rooms (server)
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min

What your eye checks

Whenever you see on, addEventListener, onmessage or once, ask:

  1. Where is it removed? Find the matching off / removeEventListener / abort(). If there isn't one, what ends its life?
  2. How many times does this line run? Inside a render, a connect handler, a function called per room or per request, it adds a listener every time.
  3. Is off given the same function object? Inline arrows and .bind() create new ones.
  4. Can it fire before the listener exists, or before the socket is open?
  5. Who handles 'error'? In Node, an emitted 'error' with no listener throws.
  6. What if two events are handled at once? Async listeners run concurrently; read-modify-write inside them races.
  7. What happens across a reconnect? Re-subscribe (in connect), don't re-register listeners, resume from the last seen id, and back off with jitter.
  8. Can events arrive twice or out of order? Make handlers idempotent and compare versions or sequence numbers.
  9. Server side: who checked that this client may join or send to this room? Room ids from the client are untrusted input.
  10. What does the connection own? Timers, subscriptions and map entries started per connection must be cleaned up on disconnect.

Tells

You seeAsk
.on( inside a function that runs more than onceHow many listeners after five calls?
.on('connect', () => { … .on(What happens on every reconnect?
off(… (x) => or .bind(this) in removeEventListenerIs it the same function?
new WebSocket(…) followed by .sendIs it open yet?
this.emit('error'Is anyone listening?
onclose = () => connect()Backoff? Jitter? A limit?
map.set(userId, socket)What about a second tab?
setInterval inside on('connection'Who clears it?
.on('x', async with read-then-write insideWhat if two arrive at once?
A version or seq field that's never readWhat if they arrive out of order?
socket.join(roomId) with a client-sent idWho checked access?

Go deeper