How Axios Manages Internal Promise Chaining
Axios handles HTTP requests and responses through an internal Promise-based execution pipeline built on a dynamic chain array. By orchestrating request interceptors, a central transport adapter, and response interceptors into a single continuous Promise chain, Axios ensures that data transformation, asynchronous operations, and error handling flow sequentially through every step of the request lifecycle.
The Interceptor and Dispatch Pipeline
At the core of Axios's request mechanism is the
Axios.prototype.request method. Historically and
architecturally, Axios models its lifecycle around an internal array
often referred to as the chain.
When a request is initiated, Axios initializes this pipeline with the central dispatch handler:
var chain = [dispatchRequest, undefined];Here, dispatchRequest is the internal function
responsible for invoking the underlying transport adapter (such as
XMLHttpRequest in the browser or the http
module in Node.js). The undefined acts as the corresponding
rejection handler for the initial adapter invocation.
Registering and Ordering Interceptors
Axios allows developers to register request and response interceptors
via axios.interceptors.request.use() and
axios.interceptors.response.use(). Each interceptor
consists of a fulfillment handler and a rejection handler.
To maintain the correct execution sequence, Axios pushes these
handlers into the chain array using different array
methods:
- Request Interceptors: Added to the front of the
array using
Array.prototype.unshift. This ensures that the most recently registered request interceptor executes first, processing configuration objects sequentially before reaching the dispatch adapter. - Response Interceptors: Appended to the end of the
array using
Array.prototype.push. This ensures response interceptors execute in the order they were registered, processing the returned response data after the adapter completes.
The resulting structure forms a symmetrical queue:
[Request Interceptors] -> [dispatchRequest, undefined] -> [Response Interceptors]
Constructing the Promise Chain
Once the chain array is populated, Axios initializes a
resolved Promise holding the request configuration:
var promise = Promise.resolve(config);
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;By shifting pairs of functions (onFulfilled and
onRejected) from the array and attaching them via
.then(), Axios constructs a linked Promise pipeline. The
output of one step becomes the input to the next.
Synchronous vs. Asynchronous Interceptor Execution
In newer versions of Axios, the engine includes an optimization for
synchronous request interceptors. If all request interceptors are marked
with synchronous: true, Axios iterates through them
synchronously rather than wrapping each in an asynchronous Promise
tick.
If any interceptor returns a Promise or does not specify the synchronous flag, Axios falls back to assembling the asynchronous Promise chain. This optimization reduces microtask scheduling overhead in high-throughput environments while preserving standard async capabilities when needed.
Transport Dispatch and Error Propagation
When the execution reaches dispatchRequest, the
configured adapter executes the network call and returns a Promise:
- Fulfillment: If the network request succeeds and matches valid HTTP status codes, the adapter resolves with the normalized response object, passing it forward to the response interceptors.
- Rejection: If a network failure occurs or the
server returns a status outside the valid range, the adapter rejects the
Promise with an
AxiosError.
Because native Promise chaining guarantees error propagation, a
rejection at any stage—whether inside a request interceptor, the
transport adapter, or a response interceptor—bypasses subsequent
fulfillment handlers and moves directly to the nearest
onRejected handler or the consumer's final
.catch() block.