The server holds the request
The whole trick lives on the server. Your client sends an ordinary request, and instead of answering, the server parks it.
Connection open, not one byte of response, until an event turns up or a timeout fires somewhere between 25 and 60 seconds later. When data arrives the parked request completes carrying it, and your client immediately sends the next one.
See what your user experiences. Updates within milliseconds of happening, because a request was already sitting there waiting to carry the answer.
Flip the arithmetic against short polling. A short poller on 5 seconds sends 12 requests a minute and most come back empty. A long poller with a 30 second timeout sends 2 a minute when nothing is happening, and every response that does arrive carries real data.
Your cost now tracks events plus a slow keepalive tick, instead of tracking the clock.
What it costs your server
Look inside your server, because that is where the work moved. A parked request is a held connection, so a server that dedicates a thread to each one drowns after a few hundred. You want an event loop or cheap lightweight threads.
Build a way to wake the right parked request when an event arrives. Inside one process that is a channel keyed by user. Across processes it becomes a pub/sub hop.
Give it credit for its age. This predates WebSocket by years, and a well-known chat product launched on it in 2008 with servers holding thousands of open requests.
Keep it in mind for compatibility, where it is still the king. Every proxy, firewall and corporate middlebox on earth understands a slow HTTP response, which is not a claim WebSocket can make.
Worked example
Lena runs support chat for a small SaaS on 3 second short polling, and transcripts show agents replying to messages that landed 2 to 3 seconds earlier, long enough to feel laggy in a live conversation. Over a weekend she converts the endpoint to long polling: the Node server parks GET /messages?since=<id> on a per-conversation channel with a 30 second timeout. Median delivery latency drops from about 1,500 ms to 90 ms, measured from database insert to render. Request volume falls too: 400 concurrent conversations used to generate 133 polls per second and now generate around 15, most of which carry actual messages. The only new client code is a while loop that re-requests as soon as each response lands.