HarmonyOS7 HTTP 请求记得 destroy:成功路径之外才是重点
文章目录
前言
HTTP 请求写通不难,难的是把请求前、请求中、请求失败和请求结束都写完整。很多示例只演示 request() 成功后怎么拿数据,但真实页面更容易出问题的地方,是重复点击、非 200 状态、异常和资源释放。
HarmonyOS7 使用 http.createHttp() 创建请求对象后,用完要记得 destroy()。我一般会把它放进 finally。网络代码不要只看成功回调,释放资源才是长期稳定的底线。
为什么网络请求最容易写成只顾眼前能跑
因为成功路径最好写。接口通了、数据出来了、列表显示了,很多人就会自然觉得这段请求逻辑已经算完成了。

但真实项目里,真正拖后腿的往往不是成功路径,而是那些顺手被省掉的小地方:重复点击怎么办,状态码异常怎么办,抛错之后怎么兜底,请求对象最后有没有释放。
所以这篇文章真正想强调的,不是“怎么把请求发出去”,而是“怎么把一次请求完整收尾”。
一个请求页应该有哪些状态
以用户列表为例,最少要准备这些状态。

| 状态 | 作用 |
|---|---|
loading |
防止重复点击,控制按钮文案 |
errorText |
展示失败原因 |
users |
渲染成功后的列表 |
responseCode 判断 |
区分有响应和业务成功 |
拿到响应不等于请求成功。接口返回 404、500,也是有响应,但页面不能当成正常数据处理。
先把请求页真正要兜住的事情想清楚
请求页最怕的不是接口一时没通,而是页面没有把这次请求前后该做的事兜住。比如按钮点下去之后要不要立刻禁用,失败后页面要不要留下可见提示,旧数据是继续保留还是清空,请求结束后资源有没有正常释放。
你先把这些问题想清楚,再看 loading、errorText、users 这些状态,就不会只把它们当成“语法上要声明的变量”。它们分别对应的是交互保护、失败反馈和成功结果,职责一旦分清,后面接真实接口时也更不容易乱。
完整 ArkTS 示例
import { http } from '@kit.NetworkKit'
interface UserInfo {
id: number
name: string
role: string
}
@Entry
@Component
struct HttpDestroyPage {
@State loading: boolean = false
@State errorText: string = ''
@State users: UserInfo[] = []
async loadUsers(): Promise<void> {
if (this.loading) {
return
}
this.loading = true
this.errorText = ''
const request = http.createHttp()
try {
const result = await request.request('https://example.com/api/users', {
method: http.RequestMethod.GET,
connectTimeout: 6000,
readTimeout: 6000
})
if (result.responseCode !== 200) {
this.errorText = `请求失败:${result.responseCode}`
return
}
this.users = [
{ id: 1, name: '林一', role: '产品经理' },
{ id: 2, name: '陈晨', role: 'HarmonyOS 开发' }
]
} catch (err) {
this.errorText = '网络异常,请稍后重试'
} finally {
request.destroy()
this.loading = false
}
}
build() {
Column({ space: 12 }) {
Row() {
Text('用户列表')
.fontSize(22)
.fontWeight(FontWeight.Bold)
Blank()
Button(this.loading ? '加载中' : '刷新')
.height(34)
.enabled(!this.loading)
.onClick(() => {
this.loadUsers()
})
}
.width('100%')
if (this.errorText.length > 0) {
Text(this.errorText)
.fontSize(13)
.fontColor('#D32F2F')
.padding(12)
.backgroundColor('#FFF0F0')
.borderRadius(8)
.width('100%')
}
List({ space: 8 }) {
ForEach(this.users, (item: UserInfo) => {
ListItem() {
Row() {
Text(item.name.substring(0, 1))
.fontSize(16)
.fontColor(Color.White)
.width(38)
.height(38)
.textAlign(TextAlign.Center)
.backgroundColor('#1E88E5')
.borderRadius(19)
Column({ space: 4 }) {
Text(item.name)
.fontSize(16)
.fontWeight(FontWeight.Medium)
Text(item.role)
.fontSize(12)
.fontColor('#777777')
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 10 })
}
.padding(14)
.backgroundColor(Color.White)
.borderRadius(10)
}
}, (item: UserInfo) => item.id.toString())
}
.layoutWeight(1)
}
.padding(16)
.backgroundColor('#F5F7FA')
.height('100%')
}
}
把关键代码一段段拆开
if (this.loading) return 是第一道保护。用户连续点刷新时,不会同时发起多次请求,列表状态也不容易被后返回的旧请求覆盖。
responseCode 要单独判断。await request.request() 成功返回,只说明拿到了响应,不代表接口业务成功。
request.destroy() 放在 finally 里。无论成功、失败、状态码异常,都会执行释放。这个位置比写在 try 末尾稳得多,因为 try 中途 return 或抛错时也不会漏掉。
示例里用本地数组模拟解析结果。真实项目中可以把 result.result 解析成 UserInfo[],但解析失败也应该进入错误处理,不要让页面静默空白。
页面销毁和请求收尾要一起考虑
这篇示例里,请求对象是在 loadUsers() 里临时创建的,所以放进 finally 里 destroy() 就够了,思路也最直观:这一轮请求不管走到哪里,最后都要把自己收干净。
如果你后面做的是上传、大文件下载、轮询或者一个页面里并发多个请求,就不能只停在这一步了。那时要继续考虑页面离开时怎么取消未完成请求、多个请求对象怎么统一管理、旧请求返回后会不会覆盖新状态。
请求失败时也不要只打印日志。日志是给开发者看的,页面提示和重试入口才是给用户看的。
新手最容易踩的坑
这一类示例最容易让人产生错觉:界面出来了,就以为已经掌握了。其实真正容易出问题的地方,通常都在效果之外,比如状态有没有收拢、失败后怎么兜底、以后要扩展时会不会牵一发动全身。
所以你练这篇内容时,别只看“现在能不能跑”,还要继续看“以后好不好改”。能把这个习惯养起来,你写出来的页面会比单纯照着示例拼出来的页面稳很多。
放进真实项目还要补什么
示例代码的重点是把核心思路讲明白,所以很多工程化细节会故意省掉。真正落到项目里时,你通常还要继续补接口联动、异常处理、边界保护、资源抽离,以及和其他页面状态之间的配合。
比较稳的做法是分三步走:先把结构和职责立住,再把真实业务接进去,最后再优化视觉和交互体验。这样改出来的页面不只是“能演示”,而是真的更接近可以长期维护的业务代码。
写在最后
HTTP 示例多写几行不是啰嗦,而是把真实项目里最容易漏的部分补上:防重复、判状态、给错误、释放资源。destroy() 这一步很小,但它决定网络请求代码能不能长期稳定。
更多推荐



所有评论(0)