Skip to content

Commit 63f59e6

Browse files
committed
- Fixed just in time cancelation scenario
1 parent 2553b2c commit 63f59e6

29 files changed

+382
-228
lines changed

src/templates/core/ApiError.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export class ApiError extends Error {
1111
constructor(response: ApiResult, message: string) {
1212
super(message);
1313

14+
this.name = 'ApiError';
1415
this.url = response.url;
1516
this.status = response.status;
1617
this.statusText = response.statusText;

src/templates/core/CancelablePromise.hbs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
11
{{>header}}
22

3+
export class CancelError extends Error {
4+
5+
constructor(reason: string = 'Promise was canceled') {
6+
super(reason);
7+
this.name = 'CancelError';
8+
}
9+
10+
public get isCancelled(): boolean {
11+
return true;
12+
}
13+
}
14+
15+
export interface OnCancel {
16+
readonly isPending: boolean;
17+
readonly isCancelled: boolean;
18+
19+
(cancelHandler: () => void): void;
20+
}
21+
322
export class CancelablePromise<T> implements Promise<T> {
423
readonly [Symbol.toStringTag]: string;
524

625
#isPending: boolean;
7-
#isCanceled: boolean;
26+
#isCancelled: boolean;
827
readonly #cancelHandlers: (() => void)[];
928
readonly #promise: Promise<T>;
1029
#resolve?: (value: T | PromiseLike<T>) => void;
@@ -14,18 +33,18 @@ export class CancelablePromise<T> implements Promise<T> {
1433
executor: (
1534
resolve: (value: T | PromiseLike<T>) => void,
1635
reject: (reason?: any) => void,
17-
onCancel: (cancelHandler: () => void) => void
36+
onCancel: OnCancel
1837
) => void
1938
) {
2039
this.#isPending = true;
21-
this.#isCanceled = false;
40+
this.#isCancelled = false;
2241
this.#cancelHandlers = [];
2342
this.#promise = new Promise<T>((resolve, reject) => {
2443
this.#resolve = resolve;
2544
this.#reject = reject;
2645

2746
const onResolve = (value: T | PromiseLike<T>): void => {
28-
if (!this.#isCanceled) {
47+
if (!this.#isCancelled) {
2948
this.#isPending = false;
3049
this.#resolve?.(value);
3150
}
@@ -42,7 +61,15 @@ export class CancelablePromise<T> implements Promise<T> {
4261
}
4362
};
4463

45-
return executor(onResolve, onReject, onCancel);
64+
Object.defineProperty(onCancel, 'isPending', {
65+
get: (): boolean => this.#isPending,
66+
});
67+
68+
Object.defineProperty(onCancel, 'isCancelled', {
69+
get: (): boolean => this.#isCancelled,
70+
});
71+
72+
return executor(onResolve, onReject, onCancel as OnCancel);
4673
});
4774
}
4875

@@ -64,10 +91,10 @@ export class CancelablePromise<T> implements Promise<T> {
6491
}
6592

6693
public cancel(): void {
67-
if (!this.#isPending || this.#isCanceled) {
94+
if (!this.#isPending || this.#isCancelled) {
6895
return;
6996
}
70-
this.#isCanceled = true;
97+
this.#isCancelled = true;
7198
if (this.#cancelHandlers.length) {
7299
try {
73100
for (const cancelHandler of this.#cancelHandlers) {
@@ -80,7 +107,7 @@ export class CancelablePromise<T> implements Promise<T> {
80107
}
81108
}
82109

83-
public get isCanceled(): boolean {
84-
return this.#isCanceled;
110+
public get isCancelled(): boolean {
111+
return this.#isCancelled;
85112
}
86113
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
function getRequestBody(options: ApiRequestOptions): any {
2+
if (options.body) {
3+
return options.body;
4+
}
5+
return;
6+
}

src/templates/core/axios/getResponseBody.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ function getResponseBody(response: AxiosResponse<any>): any {
22
if (response.status !== 204) {
33
return response.data;
44
}
5-
return null;
5+
return;
66
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
function getResponseHeader(response: AxiosResponse<any>, responseHeader?: string): string | null {
1+
function getResponseHeader(response: AxiosResponse<any>, responseHeader?: string): string | undefined {
22
if (responseHeader) {
33
const content = response.headers[responseHeader];
44
if (isString(content)) {
55
return content;
66
}
77
}
8-
return null;
8+
return;
99
}

src/templates/core/axios/request.hbs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ApiError } from './ApiError';
77
import type { ApiRequestOptions } from './ApiRequestOptions';
88
import type { ApiResult } from './ApiResult';
99
import { CancelablePromise } from './CancelablePromise';
10+
import type { OnCancel } from './CancelablePromise';
1011
import { OpenAPI } from './OpenAPI';
1112

1213
{{>functions/isDefined}}
@@ -39,6 +40,9 @@ import { OpenAPI } from './OpenAPI';
3940
{{>axios/getHeaders}}
4041

4142

43+
{{>axios/getRequestBody}}
44+
45+
4246
{{>axios/sendRequest}}
4347

4448

@@ -61,21 +65,27 @@ export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
6165
return new CancelablePromise(async (resolve, reject, onCancel) => {
6266
try {
6367
const url = getUrl(options);
64-
const response = await sendRequest(options, url, onCancel);
65-
const responseBody = getResponseBody(response);
66-
const responseHeader = getResponseHeader(response, options.responseHeader);
67-
68-
const result: ApiResult = {
69-
url,
70-
ok: isSuccess(response.status),
71-
status: response.status,
72-
statusText: response.statusText,
73-
body: responseHeader || responseBody,
74-
};
75-
76-
catchErrors(options, result);
77-
78-
resolve(result.body);
68+
const formData = getFormData(options);
69+
const body = getRequestBody(options);
70+
const headers = await getHeaders(options, formData);
71+
72+
if (!onCancel.isCancelled) {
73+
const response = await sendRequest(options, url, formData, body, headers, onCancel);
74+
const responseBody = getResponseBody(response);
75+
const responseHeader = getResponseHeader(response, options.responseHeader);
76+
77+
const result: ApiResult = {
78+
url,
79+
ok: isSuccess(response.status),
80+
status: response.status,
81+
statusText: response.statusText,
82+
body: responseHeader || responseBody,
83+
};
84+
85+
catchErrors(options, result);
86+
87+
resolve(result.body);
88+
}
7989
} catch (error) {
8090
reject(error);
8191
}

src/templates/core/axios/sendRequest.hbs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
async function sendRequest(options: ApiRequestOptions, url: string, onCancel: (cancelHandler: () => void) => void): Promise<AxiosResponse<any>> {
1+
async function sendRequest(
2+
options: ApiRequestOptions,
3+
url: string,
4+
formData: FormData | undefined,
5+
body: any,
6+
headers: Record<string, string>,
7+
onCancel: OnCancel
8+
): Promise<AxiosResponse<any>> {
29
const source = axios.CancelToken.source();
3-
const formData = options.formData && getFormData(options.formData);
4-
const data = formData || options.body;
510

611
const config: AxiosRequestConfig = {
712
url,
8-
data,
13+
headers,
14+
data: body || formData,
915
method: options.method,
10-
headers: await getHeaders(options, formData),
1116
cancelToken: source.token,
1217
};
1318

src/templates/core/fetch/getHeaders.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
3737
headers.append('Content-Type', 'application/json');
3838
}
3939
}
40+
4041
return headers;
4142
}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
2-
if (options.formData) {
3-
return getFormData(options.formData);
4-
}
5-
62
if (options.body) {
73
if (options.mediaType?.includes('/json')) {
84
return JSON.stringify(options.body)
@@ -12,6 +8,5 @@ function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
128
return JSON.stringify(options.body);
139
}
1410
}
15-
16-
return undefined;
11+
return;
1712
}

src/templates/core/fetch/getResponseBody.hbs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@ async function getResponseBody(response: Response): Promise<any> {
1414
console.error(error);
1515
}
1616
}
17-
18-
return null;
17+
return;
1918
}

0 commit comments

Comments
 (0)