HTTP is synchronous in the sense that every request gets a response, but asynchronous in the sense that requests take a long time and that multiple requests might be processed in parallel. Therefore, many HTTP clients and servers are implemented in an asynchronous fashion, but without offering an asynchronous API. Using multiple threads is one way to implement asynchronicity, but there are others like callbacks or event loops.
If you want an asynchronous API with HTTP in the sense that a response is not immediately available, you will have to model the asynchronicity explicitly:
- When a valid request is received, the server immediately responds with a success status, most likely 202 Accepted or 303 See Other.
- The backend then works on the job in the background (maybe as a separate thread, maybe as a separate server).
- The response should describe how the status of the job can be monitored and how the result can be fetched. Later, the client can use this information to retrieve the result.
There are a couple of techniques like long-polling or chunked transfer to push the result as soon as it is available from that resource, but generally requires client cooperation.
The alternative is to use a different protocol than HTTP, in particular a protocol where messages don't need a response. Within the web stack, WebSockets are such a protocol and allow the server to push a response at a later time. However, using such protocols implies a heavier development effort for you because non-HTTP APIs lack tooling and framework support.
Because async APIs are more complicated for everyone involved, they should be avoided. Again, they are not necessary for having async clients or servers. Modelling asynchronicity in your API is appropriate when:
- the request launches a background job that may or may not be processed quickly, or
- creating the response inherently takes some time, or
- you cannot guarantee that a response will be ready before the client times out, probably on the order of 10 to 30 seconds.
For example, the web interface of my WiFi router can reboot the router. This inherently takes some time. They have implemented this in the following asynchronous fashion:
- I request a reboot.
- The server responds immediately with a status page.
- The router initiates the reboot.
- The status page continuously requests the status in the background. Most of the time, the request will time out because the server has not yet restarted.
- The router reboots and the server comes back online.
- On the next status request, the server delivers a status response that the rebooting has completed, and redirects my browser to a new page.