Skip to content

Commit

Permalink
feat: abort controller (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew committed Nov 15, 2023
1 parent 704a5d7 commit 4b1457c
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,25 @@ test('Should publish error when fetch fails', (done) => {
});
});

test('Should abort previous request', async () => {
fetchMock.mockResponse(JSON.stringify(data));
const abortSpy = jest.spyOn(AbortController.prototype, 'abort');

const config: IConfig = {
url: 'http://localhost/test',
clientKey: '12',
appName: 'web',
};
const client = new UnleashClient(config);
await client.start();
client.updateContext({ userId: '123' }); // abort 1
client.updateContext({ userId: '456' }); // abort 2
await client.updateContext({ userId: '789' });

expect(abortSpy).toBeCalledTimes(2);
abortSpy.mockRestore();
});

test.each([400, 401, 403, 404, 429, 500, 502, 503])(
'Should publish error when fetch receives a %d error',
async (errorCode) => {
Expand Down
34 changes: 34 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface IConfig extends IStaticContext {
storageProvider?: IStorageProvider;
context?: IMutableContext;
fetch?: any;
createAbortController?: () => AbortController;
bootstrap?: IToggle[];
bootstrapOverride?: boolean;
headerName?: string;
Expand Down Expand Up @@ -94,6 +95,18 @@ export const resolveFetch = () => {
return undefined;
};

const resolveAbortController = () => {
try {
if (typeof window !== 'undefined' && 'AbortController' in window) {
return () => new window.AbortController();
} else if ('fetch' in globalThis) {
return () => new globalThis.AbortController();
}
} catch (e) {
console.error('Unleash failed to resolve "AbortController" factory', e);
}
};

export class UnleashClient extends TinyEmitter {
private toggles: IToggle[] = [];
private impressionDataAll: boolean;
Expand All @@ -107,6 +120,8 @@ export class UnleashClient extends TinyEmitter {
private metrics: Metrics;
private ready: Promise<void>;
private fetch: any;
private createAbortController?: () => AbortController;
private abortController?: AbortController | null;
private bootstrap?: IToggle[];
private bootstrapOverride: boolean;
private headerName: string;
Expand All @@ -128,6 +143,7 @@ export class UnleashClient extends TinyEmitter {
environment = 'default',
context,
fetch = resolveFetch(),
createAbortController = resolveAbortController(),
bootstrap,
bootstrapOverride = true,
headerName = 'Authorization',
Expand Down Expand Up @@ -176,8 +192,14 @@ export class UnleashClient extends TinyEmitter {
'Unleash: You must either provide your own "fetch" implementation or run in an environment where "fetch" is available.'
);
}
if (!createAbortController) {
console.error(
'Unleash: You must either provide your own "AbortController" implementation or run in an environment where "AbortController" is available.'
);
}

this.fetch = fetch;
this.createAbortController = createAbortController;
this.bootstrap =
bootstrap && bootstrap.length > 0 ? bootstrap : undefined;
this.bootstrapOverride = bootstrapOverride;
Expand Down Expand Up @@ -366,6 +388,15 @@ export class UnleashClient extends TinyEmitter {

private async fetchToggles() {
if (this.fetch) {
if (this.abortController) {
this.abortController.abort();
}
this.abortController =
this.createAbortController && this.createAbortController();
const signal = this.abortController
? this.abortController.signal
: undefined;

try {
const isPOST = this.usePOSTrequests;

Expand All @@ -382,6 +413,7 @@ export class UnleashClient extends TinyEmitter {
cache: 'no-cache',
headers: this.getHeaders(),
body,
signal,
});
if (response.ok && response.status !== 304) {
this.etag = response.headers.get('ETag') || '';
Expand All @@ -404,6 +436,8 @@ export class UnleashClient extends TinyEmitter {
} catch (e) {
console.error('Unleash: unable to fetch feature toggles', e);
this.emit(EVENTS.ERROR, e);
} finally {
this.abortController = null;
}
}
}
Expand Down

0 comments on commit 4b1457c

Please sign in to comment.