加载中...

Promise链式编程


省市区获取

~~运用知识点

Promise async await

Promise是一种用于处理异步操作的对象,可以将异步操作转换为类似于同步操作的风格,以方便代码编写和维护

**简而言之:**Promise 用来管理异步,方便编码

  1. 处理异步操作成功与失败
  2. 能够解决回调函数地狱

一、回调函数地狱

如果回调函数一直【嵌套】下去,代码的可读性会非常糟糕

一般在多个异步操作【彼此依赖】的时候会出现回调函数嵌套的情况,(c 依赖 b,b 依赖 a),比如:

  1. 获取所有省份
  2. 获取第一个省的所有城市
  3. 获取第一个城市的所有地区

这里的数据来源于 3 个接口,【地区】 依赖于 【城市】 、【城市】 依赖于【 省】,所以代码写起来会是这样的结果(较为繁琐复杂)

北京市地区获取

这段代码主要为获取北京的地区(这段代码回调函数会一直嵌套下去,可读性差)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import http from '@ohos.net.http'

const req = http.createHttp()

interface Province {
message: string
list: string[]
}

@Entry
@Component
struct TextPickerExample {
@State apfruits: string[] = []
orfruits: string[] = ['orange1', 'orange2', 'orange3', 'orange4']
pefruits: string[] = ['peach1', 'peach2', 'peach3', 'peach4']
@State multi: string[][] = [this.apfruits, this.orfruits, this.pefruits]

aboutToAppear(): void {
req.request('https://hmajax.itheima.net/api/province')
.then(res => {
const result = JSON.parse(res.result.toString()) as Province
//this.apfruits = result.list
// 获取省份信息
this.multi[0] = result.list
// 获取一个省份的城市信息
req.request('https://hmajax.itheima.net/api/city?pname=' + encodeURIComponent(this.multi[0][0]))
.then(res => {
const result = JSON.parse(res.result.toString()) as Province
this.multi[1] = result.list
req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.multi[0][0])}&cname=${encodeURIComponent(this.multi[1][0])}`)
.then(res => {
const result = JSON.parse(res.result.toString()) as Province
this.multi[2] = result.list
})
})
})
}

build() {
Column() {
TextPicker({ range: this.multi }).canLoop(false)
}
}
}

二、链式编程-基本使用

我们可以通过 Promise 的链式编程来解决这个问题(Promise 的 then 方法会返回一个新Promise 对象

1
2
3
4
5
6
7
8
9
promise对象1.then(res1=>{
promise对象2.then(res2=>{
promise对象3.then(res3=>{
promise对象4.then(res4=>{
//.... 可以一直写下去
})
})
})
})
北京市地区获取

上面代码aboutToAppear修改为Promise链式编程代码可读性就会提升很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
aboutToAppear(): void {
req.request('https://hmajax.itheima.net/api/province')
.then(res => {
const result = JSON.parse(res.result.toString()) as Province
// 获取省份信息
this.multi[0] = result.list
return req.request('https://hmajax.itheima.net/api/city?pname=' + encodeURIComponent(this.multi[0][0]))
})
.then(res => {
const result = JSON.parse(res.result.toString()) as Province
this.multi[1] = result.list
return req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.multi[0][0])}&cname=${encodeURIComponent(this.multi[1][0])}`)
})
.then(res => {
const result = JSON.parse(res.result.toString()) as Province
this.multi[2] = result.list
})
}
对比两种写法代码的差异

第二个aboutToAppear为修改为Promise链式编程代码的代码

三、async await 完成省市区获取

Promise 虽然不用嵌套了,但是依旧有回调函数,可以用 async 函数进一步优化

核心步骤:

  1. async 修饰函数
  2. await 等待成功(Promise 对象)

基本用法:

1
2
3
4
5
6
7
async function func() {
// await 获取到的是 之后 Promise 对象的成功结果
const res1 = await Promise对象1
const res2 = await Promise对象2
const res3 = await Promise对象3
}
func()
对比两种写法代码的差异

进一步优化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import http from '@ohos.net.http';

interface IResponse {
message: string
list: string[]
}


@Entry
@Component
struct Day03_06_AreaChange {
@State message: string = '居住地选择';
@State range: string[][] = [['北京'], ['北京市'], ['东城区']]
@State selected: number[] = [0, 0, 0]
// 这部分信息的目的是渲染到页面上
@State values: string[] = ['北京', '北京市', '东城区']
// 请求对象
req: http.HttpRequest = http.createHttp()
@State showSheet: boolean = false

async aboutToAppear() {
const Province_Res = await this.req.request('https://hmajax.itheima.net/api/province')
const result_Pro = JSON.parse(Province_Res.result.toString()) as IResponse
// 获取省份信息
this.range[0] = result_Pro.list

const city_Res =
await this.req.request('https://hmajax.itheima.net/api/city?pname=' + encodeURIComponent(this.range[0][0]))
const result_City = JSON.parse(city_Res.result.toString()) as IResponse
// 获取省份信息
this.range[1] = result_City.list

const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.range[0][0])}&cname=${encodeURIComponent(this.range[1][0])}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
}

build() {
Column({ space: 10 }) {
// 标题
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
.width('100%')
.margin({ bottom: 20 })
//
Row({ space: 10 }) {
Text('居住地:')
.fontWeight(FontWeight.Bold)
Text(this.values.join('/'))
.layoutWeight(1)
.fontColor(Color.Gray)
.onClick(() => {
this.showSheet = true
})
Image($r('app.media.ic_public_arrow_right'))
.width(20)
}

Divider()
Blank()


}
.height('100%')
.width('100%')
.alignItems(HorizontalAlign.Start)
.padding(20)
.bindSheet($$this.showSheet, this.areaSheet(), {
height: 300
})
}

@Builder
areaSheet() {
Column() {
TextPicker({
range: this.range,
selected: $$this.selected,
// value: $$this.values //
})
.canLoop(false)
}
}
}

第一种情况:改变省份信息

利用TextPicker的onChange事件(当用户滑动时改变数据触发)

这段代码考虑第一种情况:用户改变省份信息(即滑动第一列)此时需要根据用户挑选的省重新进行网络请求获取对应的城市以及地区,同时城市及地区需要指向每列第一个(上段代码中定义了一个状态变量selected与TextPicker进行双向绑定 表示指向第几个省份,城市,地区)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
   接上段代码areaSheet部分

@Builder
areaSheet() {
Column() {
TextPicker({
range: this.range,
selected: $$this.selected,
// value: $$this.values //
})
.canLoop(false)
.onChange(async (value, index) => {
// 改变省份
if (this.values[0] != value[0]) {
// 1. 刷新省份
const Province_Res = value[0]
// 省份赋值
this.values[0] = value[0]

const city_Res =
await this.req.request('https://hmajax.itheima.net/api/city?pname=' + encodeURIComponent(Province_Res))
const result_City = JSON.parse(city_Res.result.toString()) as IResponse
// 获取省份信息
this.range[1] = result_City.list
// 将城市指向到第一个
this.selected[1] = 0
// 赋值城市信息
this.values[1] = this.range[1][0]

// 查询地区信息
const cname = this.range[1][0]
const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(Province_Res)}&cname=${encodeURIComponent(cname)}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
// 将地区指向到第一个
this.selected[2] = 0
// 赋值地区
this.values[2] = this.range[2][0]
}
// console.log('这次' + value + '上次' + this.values[0]);


})
}
}
}

第二种情况:改变市

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 第二种情况 改变市
if (this.values[1] != value[1]) {
// 保存最新选中的市 避免重复触发
this.values[1] = value[1]
// 查询地区信息
const cname = value[1]
const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(value[0])}&cname=${encodeURIComponent(cname)}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
// 将地区指向到第一个
this.selected[2] = 0
// 赋值地区
this.values[2] = this.range[2][0]
return
}

第三种情况:改变地区

直接赋值到状态变量value即可

1
2
3
4
5
// 第三种 改变区
if (this.values[2] != value[2]) {
// 直接赋值value即可
this.values[2] = value[2]
}

问题:函数防抖

常见的性能优化方案 , 它可以防止高频渲染页面时出现的视觉抖动(卡顿)

为解决该问题可以设定一个定时器,如果用户在一定时间内不操作则执行逻辑(连续事件停止触发后,一段时间内如果没有再次触发,就执行业务代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@Builder
areaSheet() {
Column() {
TextPicker({
range: this.range,
selected: $$this.selected,
// value: $$this.values //
})
.canLoop(false)
.onChange(async (value, index) => {
// 清除上一次timeID
clearTimeout(this.timeID)
// 改变省份
// 函数防抖
this.timeID = setTimeout(async () => {
// 第一种情况 改变省份:市区都需要重新获取并还原到每列第一个
if (this.values[0] != value[0]) {
// 1. 刷新省份
const Province_Res = value[0]
// 省份赋值
this.values[0] = value[0]

const city_Res =
await this.req.request('https://hmajax.itheima.net/api/city?pname=' + encodeURIComponent(Province_Res))
const result_City = JSON.parse(city_Res.result.toString()) as IResponse
// 获取省份信息
this.range[1] = result_City.list
// 将城市指向到第一个
this.selected[1] = 0
// 赋值城市信息
this.values[1] = this.range[1][0]

// 查询地区信息
const cname = this.range[1][0]
const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(Province_Res)}&cname=${encodeURIComponent(cname)}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
// 将地区指向到第一个
this.selected[2] = 0
// 赋值地区
this.values[2] = this.range[2][0]

return
}

// 第二种情况 改变市
if (this.values[1] != value[1]) {
// 保存最新选中的市 避免重复触发
this.values[1] = value[1]
// 查询地区信息
const cname = value[1]
const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(value[0])}&cname=${encodeURIComponent(cname)}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
// 将地区指向到第一个
this.selected[2] = 0
// 赋值地区
this.values[2] = this.range[2][0]
return
}

// 第三种 改变区
if (this.values[2] != value[2]) {
// 直接赋值value即可
this.values[2] = value[2]
}
}, 500)
})
}
}
}

四、案例~完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151


import http from '@ohos.net.http';

interface IResponse {
message: string
list: string[]
}


@Entry
@Component
struct Day03_06_AreaChange {
@State message: string = '居住地选择';
@State range: string[][] = [['北京'], ['北京市'], ['东城区']]
@State selected: number[] = [0, 0, 0]
// 这部分信息的目的是渲染到页面上
@State values: string[] = ['北京', '北京市', '东城区']
// 请求对象
req: http.HttpRequest = http.createHttp()
@State showSheet: boolean = false
timeID: number = -1 // 设置定时器id

async aboutToAppear() {
const Province_Res = await this.req.request('https://hmajax.itheima.net/api/province')
const result_Pro = JSON.parse(Province_Res.result.toString()) as IResponse
// 获取省份信息
this.range[0] = result_Pro.list

const city_Res =
await this.req.request('https://hmajax.itheima.net/api/city?pname=' + encodeURIComponent(this.range[0][0]))
const result_City = JSON.parse(city_Res.result.toString()) as IResponse
// 获取省份信息
this.range[1] = result_City.list

const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.range[0][0])}&cname=${encodeURIComponent(this.range[1][0])}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
}

build() {
Column({ space: 10 }) {
// 标题
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
.width('100%')
.margin({ bottom: 20 })
//
Row({ space: 10 }) {
Text('居住地:')
.fontWeight(FontWeight.Bold)
Text(this.values.join('/'))
.layoutWeight(1)
.fontColor(Color.Gray)
.onClick(() => {
this.showSheet = true
})
Image($r('app.media.ic_public_arrow_right'))
.width(20)
}

Divider()
Blank()


}
.height('100%')
.width('100%')
.alignItems(HorizontalAlign.Start)
.padding(20)
.bindSheet($$this.showSheet, this.areaSheet(), {
height: 300
})
}

@Builder
areaSheet() {
Column() {
TextPicker({
range: this.range,
selected: $$this.selected,
// value: $$this.values //
})
.canLoop(false)
.onChange(async (value, index) => {
// 清除上一次timeID
clearTimeout(this.timeID)
// 改变省份
// 函数防抖
this.timeID = setTimeout(async () => {
// 第一种情况 改变省份:市区都需要重新获取并还原到每列第一个
if (this.values[0] != value[0]) {
// 1. 刷新省份
const Province_Res = value[0]
// 省份赋值
this.values[0] = value[0]

const city_Res =
await this.req.request('https://hmajax.itheima.net/api/city?pname=' + encodeURIComponent(Province_Res))
const result_City = JSON.parse(city_Res.result.toString()) as IResponse
// 获取省份信息
this.range[1] = result_City.list
// 将城市指向到第一个
this.selected[1] = 0
// 赋值城市信息
this.values[1] = this.range[1][0]

// 查询地区信息
const cname = this.range[1][0]
const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(Province_Res)}&cname=${encodeURIComponent(cname)}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
// 将地区指向到第一个
this.selected[2] = 0
// 赋值地区
this.values[2] = this.range[2][0]

return
}

// 第二种情况 改变市
if (this.values[1] != value[1]) {
// 保存最新选中的市 避免重复触发
this.values[1] = value[1]
// 查询地区信息
const cname = value[1]
const area_Res =
await this.req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(value[0])}&cname=${encodeURIComponent(cname)}`)
const result_Area = JSON.parse(area_Res.result.toString()) as IResponse
this.range[2] = result_Area.list
// 将地区指向到第一个
this.selected[2] = 0
// 赋值地区
this.values[2] = this.range[2][0]
return
}

// 第三种 改变区
if (this.values[2] != value[2]) {
// 直接赋值value即可
this.values[2] = value[2]
}
}, 500)
})
}
}
}

文章作者: 太阳神小赖
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 太阳神小赖 !
 上一篇
下厨房 下厨房
Kitchen Application Cases
2025-02-10 太阳神小赖
下一篇 
鸿蒙应用开发 鸿蒙应用开发
图片上传与下载
2025-01-31 太阳神小赖
  目录