◐ Shell
reader mode source ↗
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
88 changes: 44 additions & 44 deletions 5-network/10-long-polling/article.md
Original file line number Diff line number Diff line change
@@ -1,98 +1,98 @@
# Long polling

Long polling is the simplest way of having persistent connection with server, that doesn't use any specific protocol like WebSocket or Server Side Events.

Being very easy to implement, it's also good enough in a lot of cases.

## Regular Polling

The simplest way to get new information from the server is periodic polling. That is, regular requests to the server: "Hello, I'm here, do you have any information for me?". For example, once every 10 seconds.

In response, the server first takes a notice to itself that the client is online, and second - sends a packet of messages it got till that moment.

That works, but there are downsides:
1. Messages are passed with a delay up to 10 seconds (between requests).
2. Even if there are no messages, the server is bombed with requests every 10 seconds, even if the user switched somewhere else or is asleep. That's quite a load to handle, speaking performance-wise.

So, if we're talking about a very small service, the approach may be viable, but generally, it needs an improvement.

## Long polling

So-called "long polling" is a much better way to poll the server.

It's also very easy to implement, and delivers messages without delays.

The flow:

1. A request is sent to the server.
2. The server doesn't close the connection until it has a message to send.
3. When a message appears - the server responds to the request with it.
4. The browser makes a new request immediately.

The situation when the browser sent a request and has a pending connection with the server, is standard for this method. Only when a message is delivered, the connection is reestablished.

![](long-polling.svg)

If the connection is lost, because of, say, a network error, the browser immediately sends a new request.

A sketch of client-side `subscribe` function that makes long requests:

```js
async function subscribe() {
let response = await fetch("/subscribe");

if (response.status == 502) {
// Status 502 is a connection timeout error,
// may happen when the connection was pending for too long,
// and the remote server or a proxy closed it
// let's reconnect
await subscribe();
} else if (response.status != 200) {
// An error - let's show it
showMessage(response.statusText);
// Reconnect in one second
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Get and show the message
let message = await response.text();
showMessage(message);
// Call subscribe() again to get the next message
await subscribe();
}
}

subscribe();
```

As you can see, `subscribe` function makes a fetch, then waits for the response, handles it and calls itself again.

```warn header="Server should be ok with many pending connections"
The server architecture must be able to work with many pending connections.

Certain server architectures run one process per connection, resulting in there being as many processes as there are connections, while each process consumes quite a bit of memory. So, too many connections will just consume it all.

That's often the case for backends written in languages like PHP and Ruby.

Servers written using Node.js usually don't have such problems.

That said, it isn't a programming language issue. Most modern languages, including PHP and Ruby allow to implement a proper backend. Just please make sure that your server architecture works fine with many simultaneous connections.
```

## Demo: a chat

Here's a demo chat, you can also download it and run locally (if you're familiar with Node.js and can install modules):

[codetabs src="longpoll" height=500]

Browser code is in `browser.js`.

## Area of usage

Long polling works great in situations when messages are rare.

If messages come very often, then the chart of requesting-receiving messages, painted above, becomes saw-like.

Every message is a separate request, supplied with headers, authentication overhead, and so on.

So, in this case, another method is preferred, such as [Websocket](info:websocket) or [Server Sent Events](info:server-sent-events).
16 changes: 8 additions & 8 deletions 5-network/10-long-polling/longpoll.view/browser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Sending messages, a simple POST
function PublishForm(form, url) {

function sendMessage(message) {
Expand All @@ -18,7 +18,7 @@ function PublishForm(form, url) {
};
}

// Receiving messages with long polling
function SubscribePane(elem, url) {

function showMessage(message) {
Expand All @@ -31,18 +31,18 @@ function SubscribePane(elem, url) {
let response = await fetch(url);

if (response.status == 502) {
// Connection timeout
// happens when the connection was pending for too long
// let's reconnect
await subscribe();
} else if (response.status != 200) {
// Show Error
showMessage(response.statusText);
// Reconnect in one second
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Got message
let message = await response.text();
showMessage(message);
await subscribe();
6 changes: 3 additions & 3 deletions 5-network/10-long-polling/longpoll.view/index.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<!DOCTYPE html>
<script src="browser.js"></script>

All visitors of this page will see messages of each other.

<form name="publish">
<input type="text" name="message" />
<input type="submit" value="Send" />
</form>

<div id="subscribe">
</div>

<script>
new PublishForm(document.forms.publish, 'publish');
// random url parameter to avoid any caching issues
new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random());
</script>
Toggle all file notes Toggle all file annotations