08. Communicating with Servers

XHR/AJAX

Raw JS XMLHttpRequest` or abstraction (e.g. JQuery AJAX - Asynchronous JavaScript and XML requests).

const req = new XMLHttpRequest();
req.addEventListener("load", (response) => ...
req.open("GET", "http://example.com");
req.send();

How are multiple requests handled? TODO UNCLEAR

Event loop pattern (concurrency) is used - instead of executing all processes at once, it does things one at a time and reacting to events that happen.

(Parallelism = doing lots of things at once; concurrency = dealing with lots of things at once)

Once the response is received, it is pushed to the event queue and if there is an opening in the JS call stack, the event handler is called.

Fetch

Introduced 2017, supports promises/async.

fetch("http://example/com")
.then(res => res.json())
.then(obj => console.log(obj))
.catch(err => console.log(err));

CORS: Cross Origin Resource Sharing

By default, XHR requests can only be made to the same origin that the script is hosted on.

The server of the requested content can optionally allow send some headers to allow this, which the browser must support and enforce.

Origin: protocol (HTTP/HTTPS) + domain + port

Forbidden request-headers are those that cannot be modified by XHR. These include:

Forbidden response-headers cannot be read by XHR:

Before a XHR request is sent, the browser makes a preflight request using the HTTP OPTIONS method, to which the server should respond with some headers:

An old workaround: JSON-P (JSON with padding). Request a <script> from another origin, then a function/variable defined in the script can be called.

WebSockets

Persistent 2-way communication.

Client sends GET request to specific endpoint with a few headers (e.g. Connection: Upgrade, Upgrade: websocket, Sec-WebSocket-Key/-Protocol/-Version) to which the server response with a 101 (switching protocols) and a few more headers. After this handshake, it switches to using WebSocket.

Some NPM packages fallback to HTTP if it is not supported. With the ws package:

const WebSocket = require("ws");
const ws = new WebSocket("ws://example.com/path");

ws.on("open", () => ws.send("A"));
ws.on("message", msg => console.log(msg));