Short Polling vs. Long Polling: Understanding the Differences and When to Use Each
Table of contents
Recently I wrote about the request/response model in backend communication, but this model is not suitable for every type of request. Imagine you want to upload a 50GB video to youtube, this process is very inefficient with the request/response model because it is going to be a long request. This is where polling comes in.
Polling
Polling refers to the process of checking the status(getting new data) of a request made by the client to the server at regular intervals. This process of checking the status can occur thousands of times a second.
Types of polling
Short polling
Long polling
Some applications require real-time updates and developers often decide between using short or long polling for backend communication. Short polling is quite easy to implement but it wastes backend resources because it has to make requests several times. Long polling, on the other hand, reduces the number of requests sent but it’s more complicated to implement.
In this article, we'll explore both short polling and long polling, and discuss their pros and cons, using JavaScript as the programming language.
Short Polling
In short polling, the client sends a request to the server, and the server responds with a unique ID while the request will keep processing in the server. The client will send another request using the ID, if there are no updates, the client will send another request, and the process repeats until the server responds with a success status to the client. This technique is useful when a request takes a long time to process, such as uploading a video to YouTube.
Let's go through a code sample, In the example, I will be using JavaScript and the express framework to spin up an HTTP server
// short-poll.js
const app = require('express')();
const { v4: uuidv4 } = require('uuid');
const jobs = {
'1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed': 'pending',
'u65675654654-bbfd-4b2d-9b5d-ab8dfbbd4bed': 'done',
};
app.post('/submit', (req, res) => {
const jobId = `job:${uuidv4()}`;
jobs[jobId] = 'pending';
setTimeout(() => updateJob(jobId), 10000);
res.json({ jobId, status: jobs[jobId] });
});
app.get('/status', (req, res) => {
console.log(jobs[req.query.jobId]);
res.end('\n\nJobStatus: ' + jobs[req.query.jobId] + '%\n\n');
});
app.listen(9001, () => console.log('listening on 9001'));
function updateJob(jobId) {
jobs[jobId] = 'done';
}
The code above is a simple express server, that uses an object to store data.
app.post('/submit', (req, res) => {
const jobId = `job:${uuidv4()}`;
jobs[jobId] = 'pending';
setTimeout(() => updateJob(jobId), 10000);
res.json({ jobId, status: jobs[jobId] });
});
The route above is a POST route, that creates a job and set the current state of the job to pending then update it after 10s. The update will happen in the background but it immediately responds with the jobId which the client will use to poll.
app.get('/status', (req, res) => {
console.log(jobs[req.query.jobId]);
res.end('\n\nJobStatus: ' + jobs[req.query.jobId] + '%\n\n');
});
This route is where the client will check the status of the job, In our example, we have two different states (pending and done). The client will keep sending requests to this route till the server responds with the done status.
To start the server, run node short-poll.js
Now let's test out the code. I will be using postman as the client.
Send a POST request to this URL
localhost:9001/submit
It will respond with the jobId. This jobId is what we will use to poll.
- Send a GET request to
localhost:9001/status?jobId=jobId
- It will return the current state of the job whether it's pending or done. keep doing step 3 till the server responds with done.
In this short example, we have seen how easy to implement and how inefficient and resource-wasteful short polling is, as it generates a large number of requests, even when there are no updates.
Long Polling
With Long polling, the client sends a request to the server, if the response required is not ready, the server will keep the request open till there is a suitable response, when the server has the response required, the server will send the response back. This reduces the number of requests sent by the client, making it more efficient than short polling.
Let's go through a simple example of how long polling work with JavaScript
// long-polling.js
const app = require('express')();
const { v4: uuidv4 } = require('uuid');
const jobs = {};
app.post('/submit', (req, res) => {
const jobId = `job:${uuidv4()}`;
jobs[jobId] = 'pending';
setTimeout(() => updateJob(jobId), 20000);
res.json({ jobId, status: jobs[jobId] });
});
app.get('/status', async (req, res) => {
const jobId = req.query.jobId;
//long polling, don't respond until done
while ((await checkJobComplete(req.query.jobId)) == false);
res.json({ jobId, status: jobs[jobId] });
});
app.listen(9002, () => console.log('listening on 9002'));
async function checkJobComplete(jobId) {
return new Promise((resolve, reject) => {
if (jobs[jobId] !== 'done') this.setTimeout(() => resolve(false), 1000);
else resolve(true);
});
}
function updateJob(jobId) {
jobs[jobId] = 'done';
}
The code above is similar to the short polling code, the major difference is how we check for the job status.
app.post('/submit', (req, res) => {
const jobId = `job:${uuidv4()}`;
jobs[jobId] = 'pending';
setTimeout(() => updateJob(jobId), 20000);
res.json({ jobId, status: jobs[jobId] });
});
This route submits the job to the server, it returns the jobId and status almost immediately while it updates the job status in the background after the 20s(20000ms).
app.get('/status', async (req, res) => {
const jobId = req.query.jobId;
//long polling, don't respond until done
while ((await checkJobComplete(req.query.jobId)) == false);
res.json({ jobId, status: jobs[jobId] });
});
This is where the long polling happens, this route checks for the status of the job, When a client sends a GET request here, it will keep the connection open with the while
loop till it gets a status of done, then it returns to the client then closes the connection.
async function checkJobComplete(jobId) {
return new Promise((resolve, reject) => {
if (jobs[jobId] !== 'done') this.setTimeout(() => resolve(false), 1000);
else resolve(true);
});
}
This helper function takes a jobId as an argument which is used to check whether a specific job is complete or not. The function returns a Promise that resolves to a Boolean value indicating whether the job is complete or not.
Now let's test, I will use postman as the client.
Run the server
node long-polling.js
Send a POST request to this URL
localhost:9002/submit
It will respond with the jobId. This jobId is what we will use to poll.
Then the client will send another GET request to
localhost:9002/status?jobId=<jobId>
This request will not return until it gets a good response from the backend
From the above example, Long polling reduced the number of requests made to the server by the client, making it more efficient than short polling. The downside of this approach is that it can be more complex to implement because the connection needs to be open on the server side till there is new data to be sent to the client.
Conclusion
When building applications that are real-time, polling can be an option to use and developers will decide on whether to use short polling or long polling to communicate between the client and server side. Short polling can be easy to implement, but it can become inefficient and wasteful of backend resources when too many requests start going in. Long polling, on the other hand, is quite efficient than short polling, because of the reduced number of requests, but it can be more complex to implement.
Code source:
https://github.com/rammyblog/polling-example
References
https://www.udemy.com/course/fundamentals-of-backend-communications-and-protocols/