Table of content
JavaScript is inherently single-threaded, meaning it can only run one piece of code at a time. Some operations — like fetching data from a server or reading a file — can take a long time. If these were run synchronously, they would block everything else from running.
Asynchronous programming allows these long-running operations to be handed off to the browser’s internal systems — for example, the networking layer for HTTP requests — so the JavaScript engine can keep executing other code without being blocked. While these operations are in progress, the browser manages them separately, and the results are delivered back to JavaScript later through callbacks, promises, or async/await
.
Modern browsers are full runtime environments that do far more than just run JavaScript. They handle rendering, networking, event handling, security, and many other subsystems. You can read more about this in MDN’s article on browser architecture.
XMLHttpRequest
is a built-in object on JavaScript that provides an easy way to make HTTP requests from the browser. It supports both synchronous and asynchronous requests, but asynchronous usage is more common and recommended to avoid blocking the main thread. It has been available for a long time and was the standard way to perform HTTP requests before the introduction of the Fetch
API.
While XMLHttpRequest
is still functional and supported in most browsers, the Fetch
API is considered more modern, provides a simpler and more flexible interface, and is based on Promises. The Fetch
API is generally recommended for new projects, but XMLHttpRequest
may still be encountered in older codebases.
Link to Documentation on XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos', true);
xhr.addEventListener('readystatechange', () => {
// The request is complete and the response is available
if (xhr.readyState === 4) {
// Succesful request
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
// Request failed
console.error('Error:', xhr.status);
}
}
});
xhr.send();
Down below is a list of the different properties, methods and event related to the XMLHttpRequest
.
- readyState
Represents the state of the request. It can have values from 0 to 4, indicating different states of the request lifecycle. Here’s your list with the small tweak applied so it’s fully accurate:
-
0: UNSENT – Request not initialized (
open()
has not been called). -
1: OPENED – Request has been configured with
open()
; connection not necessarily established yet. -
2: HEADERS_RECEIVED –
send()
has been called and the HTTP headers + status are available. -
3: LOADING – Response body is being received; partial
responseText
may be available. -
4: DONE – Request completed (either successfully or with an error).
- responseText
Returns the response as a string. This is the data that has been fetched. In order to convert this string to the underlying data structure (object, array or whatever it is) you need to use the JSON.parse
method.
- status
Returns the HTTP status code of the response (e.g., 200 for a successful request).
- statusText
Returns the HTTP status text of the response (e.g., "OK" for a successful request).
- open( method, url, async, user, password )
Initializes a request.
method
: The HTTP method (e.g., "GET", "POST").
url
: The URL of the resource.
async
: Whether the request should be asynchronous (true) or synchronous (false).
user
and password
: Optional parameters for authentication.
- send( data ):
Sends the request.
data
: Optional data to send with a POST request.
- setRequestHeader( header, value ):
Sets a request header. This method must be called after open() and before send().
-
readystatechange – fires on every state change (readyState 0→4).
-
load – fires when the request finishes successfully (after state 4 with a 2xx status).
-
error – fires if the request fails due to a network error.
-
abort – fires if you call xhr.abort().
-
timeout – fires if the request times out (xhr.timeout set, and exceeded).
-
loadstart – fires when the request starts.
-
loadend – fires when the request finishes, regardless of success or error.
progress – fires during the download phase with progress info (only works if server sends Content-Length).
A Promise is an object that represents the eventual completion or failure of an asynchronous operation. It is a way to handle asynchronous code in a more organized and readable manner. A Promise can be in one of three states:
- Pending:
The initial state when a Promise is created.
- Fulfilled ( Resolved ):
The state when the asynchronous operation is successfully completed, and the Promise has a resulting value.
- Rejected:
The state when the asynchronous operation encounters an error or fails, and the Promise has a reason for the failure.
You can create a Promise using the Promise constructor, which takes a function as its argument. This function, often called the "executor," takes two argument: resolve and reject. These are functions provided by the Promise implementation.
import cars from './cars.js';
const customPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const data = cars;
resolve(data);
reject(new Error('Something went wrong'));
}, 2000);
});
Once you have a Promise, you can use the .then()
and .catch()
methods to handle the successful and unsuccessful outcomes, respectively.
customPromise
.then((data) => {
console.log('Success:', data);
})
.then(() => {})
.then(() => {})
.catch((error) => {
console.error('Error:', error);
});
You could chain multiple .then()
calls to create a sequence of asynchronous operations. This chaining helps avoid "callback hell" and makes asynchronous code more readable and maintainable.
Promises are a fundamental part of modern JavaScript, and they provide a more structured way to handle asynchronous code compared to traditional callback patterns.
fetch
is a modern web API in JavaScript designed for making network requests (HTTP requests) from a web browser. It provides a cleaner and more powerful alternative to the older XMLHttpRequest for handling asynchronous operations.
-
Returns a promise: it returns a
Promise
and this promise resolves to aResponse
object representing the completion or failure of the request. -
Promise-Based Asynchronous Pettern:
fetch
uses ths pattern and allows you to use.then()
and.catch()
methods for handling the success and failure of the asynchronous operation. -
No Callback Hell: unlike the traditional callback pattern,
fetch
helps avoid "callback hell" by allowing you to chain multiple.then()
calls, creating a more readable and maintainable code structure. -
Simplified Syntax: the syntax of
fetch
is more straightforward and cleaner compared toXMLHttpRequest
. It uses a simple and consistent API, making it easier to work with. -
Flexibility with Options Object: the
fetch
function takes a URL as its first parameter and an optional options object as its second parameter. Thisoptions
object allows you to configure various aspects of the request, such as method, headers, and more. -
Response Object: the
Response
object returned byfetch
represents the response to the request. It provides various properties and methods for inspecting and manipulating the response, such as checking the status code, reading the response body, and handling different data formats. -
Modern Replacement for
XMLHttpRequest
:fetch
is considered a modern replacement for the older XMLHttpRequest due to its simplicity, consistency, and integration with Promises. -
async/await
Compatibility:fetch
works seamlessly with async/await, allowing you to write asynchronous code in a more synchronous style, making it even more readable.
In summary, fetch is a versatile and efficient API for making HTTP requests in JavaScript. Its Promise-based design simplifies asynchronous code, making it a preferred choice for handling network requests in modern web development.
fetch( url, options?)
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error("Error:", error))
// or
async function fetchSomething(){
try {
const response = await fetch( url, options? );
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Error:", error);
}
}
The url
is the URL of the resource you want to fetch. options
is an optional object that contains settings for the request, such as headers, method and body etc.
The fetch
returns a Promise that resolves to the Response
to taht request, whether it is successful or not.
-
method: the HTTP method for the request ( "GET", "POST", "PUT", "DELETE" ).
GET
is the default value on this property.GET
is used for fetching resourcesPOST
is used for creating new resourcesPUT
is used for updating existing resourcesDELETE
is used to remove resources
fetch('https://api.example.com/data', {
method: 'POST',
});
- headers: an object containing any headers you want to include in your request. The most common header is the
Content/Type
which specifies the media type of the resource. For example, when sending JSON data in aPOST
request, you would set this property toapplication/json
.
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
- body: data to be sent in a request, usually a
POST
orPUT
request. It is most of the times a JSON string but can have other forms as well.
The Response
object represents the response to a request. It has various properties and methods, here is a few of them.
-
ok: a boolean indicating whether the request was successful ( status code in the range 200-299 ) or not.
-
status: the HTTP status code of the response.
-
statusText: the HTTP status text of the response.
-
headers: an object representing the headers of the response.
-
json(): a method that returns a Promise that resolves to the result of parsing the response body as JSON.
An Application Programming Interface (API) is a set of rules and protocols that allows different software applications to communicate with each other. It defines the methods and data formats that applications can use to request and exchange information. APIs are used to enable the integration of different software systems, allowing them to work together and share data seamlessly.
In the context of JavaScript, APIs are commonly used to interact with web services or external resources.
APIs expose specific endpoints ( URLs ) that correspond to different functionalities or data. These endpoints are like addresses you can visit to perform specific actions or retrieve specific information.
When you make a request to an API endpoint, you send a request with specific parameters ( such as query parameters or data in the request body ). The API processes the request and sends back a response with the requested data or the result of the action.
APIs often use standard data formats for the exchange of information. JSON ( JavaScript Object Notation ) is a common format for structuring data in API responses.
This is a free fake API that you can use in your application for testing and prototyping your application. It contains different resources that have some relations to each other.
Here is the Guide to json placeholder.