Skip to content

Commit

Permalink
feat: partial sync execution to req interceptors
Browse files Browse the repository at this point in the history
  • Loading branch information
dfidalg0 committed Jul 2, 2023
1 parent 21a5ad3 commit 0673a21
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 40 deletions.
77 changes: 42 additions & 35 deletions lib/core/Axios.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,21 @@ class Axios {

// filter out skipped interceptors
const requestInterceptorChain = [];
let synchronousRequestInterceptors = true;

// We'll keep this to track the position of the last asynchronous request
// interceptor
let requestInterceptorsCount = 0;
let asyncInterceptorsThreshold = 0;
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
return;
}

synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
requestInterceptorsCount++;

if (!interceptor.synchronous) {
asyncInterceptorsThreshold = requestInterceptorsCount;
}

requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
Expand All @@ -108,53 +116,52 @@ class Axios {
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});

let promise;
let i = 0;
let len;

if (!synchronousRequestInterceptors) {
const chain = [dispatchRequest.bind(this), undefined];
chain.unshift.apply(chain, requestInterceptorChain);
chain.push.apply(chain, responseInterceptorChain);
len = chain.length;

promise = Promise.resolve(config);

while (i < len) {
promise = promise.then(chain[i++], chain[i++]);
}
// Since we are always unshifting two elements into the request interceptor
// chain, we must decrease 2 times the threshold amount of async interceptors
// to get the synchronous request interceptor chain length.
const syncLen = requestInterceptorChain.length - 2 * asyncInterceptorsThreshold;

return promise;
}
const chain = [dispatchRequest.bind(this), undefined];

len = requestInterceptorChain.length;
chain.unshift.apply(chain, requestInterceptorChain);
chain.push.apply(chain, responseInterceptorChain);

let newConfig = config;
const totalLen = chain.length;

i = 0;
let i = 0;

while (i < len) {
const onFulfilled = requestInterceptorChain[i++];
const onRejected = requestInterceptorChain[i++];
while (i < syncLen) {
const onFulfilled = chain[i++];
const onRejected = chain[i++];
try {
newConfig = onFulfilled(newConfig);
config = onFulfilled(config);
} catch (error) {
onRejected.call(this, error);
break;
}
}

try {
promise = dispatchRequest.call(this, newConfig);
} catch (error) {
return Promise.reject(error);
}
let promise;

i = 0;
len = responseInterceptorChain.length;
if (i < requestInterceptorChain.length) {
// There are asynchronous request interceptors before the request dispatcher
// itself is called.
promise = Promise.resolve(config);
}
else {
// All the request interceptors were executed synchronously. Then, the
// next element on the interceptor chain will be the request dispatcher
// itself and, in this case, we must call it synchronously.
try {
i += 2;
promise = dispatchRequest.call(this, config);
} catch (error) {
return Promise.reject(error);
}
}

while (i < len) {
promise = promise.then(responseInterceptorChain[i++], responseInterceptorChain[i++]);
while (i < totalLen) {
promise = promise.then(chain[i++], chain[i++]);
}

return promise;
Expand Down
27 changes: 22 additions & 5 deletions test/specs/interceptors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,41 @@ describe('interceptors', function () {
});
});

it('should execute asynchronously when not all interceptors are explicitly flagged as synchronous', function (done) {
it('should execute synchronous interceptors added after async interceptors synchronously', function (done) {
let asyncFlag = false;

axios.interceptors.request.use(function (config) {
config.headers.foo = 'uh oh, async';
config.headers.test = 'added by async interceptor';
expect(asyncFlag).toBe(true);
return config;
});

axios.interceptors.request.use(function (config) {
config.headers.test = 'added by synchronous interceptor';
expect(asyncFlag).toBe(false);
return config;
}, null, { synchronous: true });

axios('/foo');
asyncFlag = true;

getAjaxRequest().then(function (request) {
expect(request.requestHeaders.test).toBe('added by async interceptor');
done();
});
});

it('should execute synchronous interceptors added before async interceptors asynchronously', function (done) {
let asyncFlag = false;

axios.interceptors.request.use(function (config) {
config.headers.test = 'added by synchronous interceptor';
expect(asyncFlag).toBe(true);
return config;
}, null, { synchronous: true });

axios.interceptors.request.use(function (config) {
config.headers.test = 'added by the async interceptor';
config.headers.test = 'added by async interceptor';
expect(asyncFlag).toBe(true);
return config;
});
Expand All @@ -84,8 +103,6 @@ describe('interceptors', function () {
asyncFlag = true;

getAjaxRequest().then(function (request) {
expect(request.requestHeaders.foo).toBe('uh oh, async');
/* request interceptors have a reversed execution order */
expect(request.requestHeaders.test).toBe('added by synchronous interceptor');
done();
});
Expand Down

0 comments on commit 0673a21

Please sign in to comment.