From feafe3a382cdc128a6ab9dddd3338b889c53fac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Wed, 21 Jan 2026 17:23:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor(http):=20=E4=BC=98=E5=8C=96HTTP?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E7=AE=A1=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入PendingRequest机制处理并发请求 - 添加序列号生成器防止请求冲突 - 实现请求等待队列管理多个Promise - 更新API工具类使用新的日志记录方式 - 修改登录相关API编号匹配逻辑 - 重构请求取消和清理流程 - 优化存储本地HTTP处理器列表的方式 --- src/main/ets/http/HttpHelper.ets | 386 ++++++++++++++++++++++--------- 1 file changed, 282 insertions(+), 104 deletions(-) diff --git a/src/main/ets/http/HttpHelper.ets b/src/main/ets/http/HttpHelper.ets index 4e44107..2ea5ad6 100644 --- a/src/main/ets/http/HttpHelper.ets +++ b/src/main/ets/http/HttpHelper.ets @@ -7,6 +7,19 @@ import { HttpHelperX, HttpParamsForm, HttpParamsGet, HttpParamsPost, HttpParamsU import { BusinessError } from '@kit.BasicServicesKit'; import { image } from '@kit.ImageKit'; +type AnyResult = object | string | number | boolean | null | undefined; + +interface Waiters { + resolve: (v: AnyResult) => void; + reject: (e: Error) => void; +} + +interface PendingRequest { + http?: http.HttpRequest + waiters: Array + seq: number +} + export class HttpHelper { private static instance: HttpHelper | null = null @@ -24,9 +37,10 @@ export class HttpHelper { private httpHandlerList = new HashMap(); // 并发白名单,这个名单里面的api,重复请求不会取消 private concurrentList = new ArrayList(); + private pendingMap = new HashMap(); + private seqGen = 0; constructor() { - this.httpHandlerList = new HashMap(); SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length) try { @@ -50,6 +64,7 @@ export class HttpHelper { } catch (error) { } } + public addConcurrents(apiNo: string[]) { for (let apiNoElement of apiNo) { try { @@ -69,6 +84,7 @@ export class HttpHelper { } catch (error) { } } + public removeConcurrents(apiNo: string[]) { for (let apiNoElement of apiNo) { try { @@ -88,19 +104,20 @@ export class HttpHelper { * @returns */ public postJson(params: HttpParamsPost, apiNo?: string, showLog?: boolean): Promise { - return new Promise((resolve, reject) => { let httpRequest = http.createHttp(); - this.setHandler(apiNo ?? params.url, httpRequest) - + const key = apiNo ?? params.url + const pending = this.setHandler(key, httpRequest, { + resolve: (v: AnyResult) => resolve(v as T), + reject: (e: Error) => reject(e) + }) const header = HttpHelperX.getHeaders("application/json;charset=UTF-8", params.headers) if (showLog) { LogHelper.debug(`postJson:${apiNo}\n`, JSON.stringify(params)) } - httpRequest.request(HttpHelperX.getUrl(params.url, params.query), { method: http.RequestMethod.POST, connectTimeout: 60000, @@ -108,66 +125,120 @@ export class HttpHelper { header: header, extraData: params.data, usingCache: false, - }) - .then((data: http.HttpResponse) => { - if (showLog) { - LogHelper.debug(`${apiNo}:\n ${data.result as string}`) - LogHelper.print(data) - } + }).then((data: http.HttpResponse) => { + const latest = this.pendingMap.get(key) + if (showLog && (!pending || (latest && latest.seq === pending.seq))) { + LogHelper.debug(`${apiNo}:\n ${data.result as string}`) + LogHelper.print(data) + } - if (data.responseCode === 200) { - resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) + if (data.responseCode === 200) { + const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T + + if (!pending) { + resolve(result) } else { - const err: Error = new Error() - err.name = data.responseCode.toString() - err.message = '服务异常' - reject(err) + if (!latest || latest.seq !== pending.seq) { + return + } + const result1 = result as AnyResult + latest.waiters.forEach(w => w.resolve(result1)) } - }).catch((err: Error) => { - LogHelper.error(JSON.stringify({ err: err, url: params.url, })) - if (err.message === 'Failed writing received data to disk/application') { - const error: Error = new Error() - error.name = 'cancel' - error.message = err.message - reject(error) } else { - reject(err) + const err: Error = new Error() + err.name = data.responseCode.toString() + err.message = '服务异常' + if (!pending) { + reject(err) + } else { + if (!latest || latest.seq !== pending.seq) { + return + } + latest.waiters.forEach(w => w.reject(err)) + } + } + + }).catch((err: Error) => { + + let e = err + if (err.message === 'Failed writing received data to disk/application') { + e.name = 'cancel' + e.message = err.message + } + + if (!pending) { + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + reject(e) + } else { + const latest = this.pendingMap.get(key) + if (!latest || latest.seq !== pending.seq) { + return + } + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + latest.waiters.forEach(w => w.reject(e)) } }).finally(() => { - try { - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + + if (pending) { + const latest = this.pendingMap.get(key) + if (latest && latest.seq === pending.seq) { + this.pendingMap.remove(key) + this.httpHandlerList.remove(key) } - } catch (error) { + } else { + this.httpHandlerList.remove(key) } - }); - }); + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate( + SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length + ) + }) + }) } - setHandler(key: string, httpRequest: http.HttpRequest) { - try { - if (this.concurrentList.getIndexOf(key) === -1 && - this.httpHandlerList.hasKey(key)) { - this.httpHandlerList.get(key).destroy() - this.httpHandlerList.remove(key) - } - } catch (error) { + setHandler(key: string, httpRequest: http.HttpRequest, waiters: Waiters): PendingRequest | null { + // 白名单:完全绕过 + if (this.concurrentList.getIndexOf(key) !== -1) { + return null } - try { - if (this.concurrentList.getIndexOf(key) === -1) { - this.httpHandlerList.set(key, httpRequest) - } - } catch (error) { + if (this.seqGen > 99999) { + this.seqGen = 0 } + const seq = ++this.seqGen + let pending = this.pendingMap.get(key) + + if (!pending) { + pending = { + waiters: [waiters], + seq, + http: httpRequest + } + this.pendingMap.set(key, pending) + } else { + // cancel 旧请求 + const h = pending.http + pending = { + waiters: [...pending.waiters, waiters], + seq, + http: httpRequest + } + this.pendingMap.set(key, pending) + h?.destroy() + } + + this.httpHandlerList.set(key, httpRequest) + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + SZYXLocalStorageHelper.storage.setOrCreate( + SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length + ) + return pending } + /** * postForm请求 * @param url url地址 @@ -180,7 +251,11 @@ export class HttpHelper { return new Promise((resolve, reject) => { let httpRequest = http.createHttp(); - this.setHandler(apiNo ?? params.url, httpRequest) + const key = apiNo ?? params.url + const pending = this.setHandler(key, httpRequest, { + resolve: (v: AnyResult) => resolve(v as T), + reject: (e: Error) => reject(e) + }) const header = HttpHelperX.getHeaders("application/x-www-form-urlencoded;charset=UTF-8", params.headers) let data = HttpHelperX.getContent(params.data) @@ -198,39 +273,73 @@ export class HttpHelper { extraData: data ? encodeURI(data) : undefined }) .then((data: http.HttpResponse) => { - if (showLog) { + const latest = this.pendingMap.get(key) + if (showLog && (!pending || (latest && latest.seq === pending.seq))) { LogHelper.debug(`${apiNo}:\n ${data.result as string}`) LogHelper.print(data) } if (data.responseCode === 200) { - resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) + const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T + + if (!pending) { + resolve(result) + } else { + if (!latest || latest.seq !== pending.seq) { + return + } + const result1 = result as AnyResult + latest.waiters.forEach(w => w.resolve(result1)) + } } else { const err: Error = new Error() err.name = data.responseCode.toString() err.message = '服务异常' - reject(err) + if (!pending) { + reject(err) + } else { + if (!latest || latest.seq !== pending.seq) { + return + } + latest.waiters.forEach(w => w.reject(err)) + } } }).catch((err: Error) => { - LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + + let e = err if (err.message === 'Failed writing received data to disk/application') { - const error: Error = new Error() - error.name = 'cancel' - error.message = err.message - reject(error) + e.name = 'cancel' + e.message = err.message + } + + if (!pending) { + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + reject(e) } else { - reject(err) + const latest = this.pendingMap.get(key) + if (!latest || latest.seq !== pending.seq) { + return + } + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + latest.waiters.forEach(w => w.reject(e)) } }).finally(() => { - try { - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + + if (pending) { + const latest = this.pendingMap.get(key) + if (latest && latest.seq === pending.seq) { + this.pendingMap.remove(key) + this.httpHandlerList.remove(key) } - } catch (error) { + } else { + this.httpHandlerList.remove(key) } + + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate( + SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length + ) }); }); @@ -250,7 +359,11 @@ export class HttpHelper { return new Promise((resolve, reject) => { let httpRequest = http.createHttp(); - this.setHandler(apiNo ?? params.url, httpRequest) + const key = apiNo ?? params.url + const pending = this.setHandler(key, httpRequest, { + resolve: (v: AnyResult) => resolve(v as T), + reject: (e: Error) => reject(e) + }) if (showLog) { LogHelper.debug(`GET:${apiNo}\n`, HttpHelperX.getUrl(params.url, params.query) + '\n', @@ -266,44 +379,73 @@ export class HttpHelper { // extraData: params.data }) .then((data: http.HttpResponse) => { - if (showLog) { - LogHelper.debug(`${apiNo}:\n${data.result as string}`) + const latest = this.pendingMap.get(key) + if (showLog && (!pending || (latest && latest.seq === pending.seq))) { + LogHelper.debug(`${apiNo}:\n ${data.result as string}`) LogHelper.print(data) } if (data.responseCode === 200) { + const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T - if (typeof data.result === 'string') { - resolve(JSON.parse(data.result) as T) + if (!pending) { + resolve(result) } else { - resolve(data.result as T) + if (!latest || latest.seq !== pending.seq) { + return + } + const result1 = result as AnyResult + latest.waiters.forEach(w => w.resolve(result1)) } } else { const err: Error = new Error() err.name = data.responseCode.toString() err.message = '服务异常' - reject(err) + if (!pending) { + reject(err) + } else { + if (!latest || latest.seq !== pending.seq) { + return + } + latest.waiters.forEach(w => w.reject(err)) + } } }).catch((err: Error) => { - LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + + let e = err if (err.message === 'Failed writing received data to disk/application') { - const error: Error = new Error() - error.name = 'cancel' - error.message = err.message - reject(error) + e.name = 'cancel' + e.message = err.message + } + + if (!pending) { + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + reject(e) } else { - reject(err) + const latest = this.pendingMap.get(key) + if (!latest || latest.seq !== pending.seq) { + return + } + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + latest.waiters.forEach(w => w.reject(e)) } }).finally(() => { - try { - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + + if (pending) { + const latest = this.pendingMap.get(key) + if (latest && latest.seq === pending.seq) { + this.pendingMap.remove(key) + this.httpHandlerList.remove(key) } - } catch (error) { + } else { + this.httpHandlerList.remove(key) } + + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate( + SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length + ) }); }); @@ -321,7 +463,11 @@ export class HttpHelper { return new Promise((resolve, reject) => { let httpRequest = http.createHttp(); - this.setHandler(apiNo ?? params.url, httpRequest) + const key = apiNo ?? params.url + const pending = this.setHandler(key, httpRequest, { + resolve: (v: AnyResult) => resolve(v as T), + reject: (e: Error) => reject(e) + }) if (showLog) { LogHelper.debug(`postJson:${apiNo}\n`, JSON.stringify(params)) @@ -341,40 +487,72 @@ export class HttpHelper { multiFormDataList: params.data, }) .then((data: http.HttpResponse) => { - if (showLog) { + const latest = this.pendingMap.get(key) + if (showLog && (!pending || (latest && latest.seq === pending.seq))) { LogHelper.debug(`${apiNo}:\n ${data.result as string}`) LogHelper.print(data) } if (data.responseCode === 200) { - resolve((typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T) + const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T + + if (!pending) { + resolve(result) + } else { + if (!latest || latest.seq !== pending.seq) { + return + } + const result1 = result as AnyResult + latest.waiters.forEach(w => w.resolve(result1)) + } } else { const err: Error = new Error() err.name = data.responseCode.toString() err.message = '服务异常' - reject(err) + if (!pending) { + reject(err) + } else { + if (!latest || latest.seq !== pending.seq) { + return + } + latest.waiters.forEach(w => w.reject(err)) + } } }).catch((err: Error) => { - LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + let e = err if (err.message === 'Failed writing received data to disk/application') { - const error: Error = new Error() - error.name = 'cancel' - error.message = err.message - reject(error) + e.name = 'cancel' + e.message = err.message + } + + if (!pending) { + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + reject(e) } else { - reject(err) + const latest = this.pendingMap.get(key) + if (!latest || latest.seq !== pending.seq) { + return + } + LogHelper.error(JSON.stringify({ err: err, url: params.url, })) + latest.waiters.forEach(w => w.reject(e)) } }).finally(() => { - httpRequest.off("dataSendProgress"); - try { - if (this.httpHandlerList.hasKey(apiNo ?? params.url)) { - this.httpHandlerList.remove(apiNo ?? params.url) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) - SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, - this.httpHandlerList.length) + + if (pending) { + const latest = this.pendingMap.get(key) + if (latest && latest.seq === pending.seq) { + this.pendingMap.remove(key) + this.httpHandlerList.remove(key) } - } catch (error) { + } else { + this.httpHandlerList.remove(key) } + + SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList) + SZYXLocalStorageHelper.storage.setOrCreate( + SZYXLocalStorageKeys.HttpHandlerListLength, + this.httpHandlerList.length + ) }); });