1 Promise是什么
1.1 基本概念
Promise 是用来管理异步操作的对象,可以获取成功(或失败)的结果
核心代码:
const p = new Promise<string>((resolve, reject) => {
// 执行任意代码,主要是异步
// 成功 resolve(成功结果) then 执行
// 失败 reject(失败结果) then第二个回调函数或 catch 执行
})
// 成功
p.then(res => {})
// 失败
p.then(res => {}, (err: string) => {})
p.then(res=>{}).catch((err:string)=>{})
1.2 作用
能够解决回调函数地狱(多层回调函数嵌套)
// 回调地狱写法
const req = http.createHttp()
req.request('接口1')
.then(res1=>{
req.request('接口2?p=接口1的结果res1')
.then(res2=>{
req.request('接口3?p=接口2的结果res2')
.then(res3=>{
req.request('接口4?p=接口3的结果res3')
.then(res4=>{
req.request('接口5?p=接口4的结果res4')
.then(res5=>{
req.request('接口6?p=接口5的结果res5')
.then(res6=>{
})
})
})
})
})
})
1.3 使用
核心三步走
- 内部执行异步代码
- 传递成功结果
- 传递失败结果
// 1. 内部执行异步代码
const p = new Promise<string>((resolve,reject) => {
// 执行任意代码,主要是异步
setTimeout(() => {
// 比如:获取随机数
const randomNum = Math.floor(Math.random() * 100)
// resolve(randomNum.toString())
reject(randomNum.toString())
},2000)
})
// 2. 传递成功结果
p.then(res => {
console.log('res:', res)
})
// 3. 传递失败结果
.catch((err:string)=>{
console.log('err:',err)
})
2 Promise 的状态
Promise 必然处于 3 种状态中的某一种,调用resolve,reject 的本质就是更改他的状态
Promise的三种状态
- 待定(pending): 初始状态,既没有被兑现(resolve),也没有被拒绝(reject)
- 已兑现(fullfilled): 意味着操作成功完成
- 已拒绝(rejected): 意味着操作失败
注意事项:
状态的改变不可逆
调用 resolve 之后再调用 reject,状态还是 已兑现,反之亦然
拓展
直接得到一个成功或者失败的结果写法:
Promise.resolve(成功信息) //直接得到一个已兑现的Promise对象
Promise.reject(失败信息) //直接得到一个已拒绝的Promise对象
3 链式编程
3.1 什么是链式编程
链式编程是在一个对象上可以重复点出方法,这些方法返回同一个类型的对象
特点
- 后面的 .then是可以接受到前面.then返回的成功状态下的值
- 后面的.catch是可以接受前面.then中返回的失败状态下的值
- 一旦在某个.then里面返回的是一个失败状态的Pormise,则直接跳过其他的.then进入到.catch执行
总结
写法: p.then().then()....catch() 注意点:如果 后一个.then需要用到前一个.then中的结果,需要在前一个.then中 显示return一下
let p = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('成功1')
}, 10)
})
p
.then(res1 => {
console.log('res1--->', res1)
// return '成功2' // 等价于:return Promise.resolve('成功2')
return Promise.reject('失败1')
})
.then(res2 => {
console.log('res2--->', res2)
return Promise.resolve('成功3')
// return Promise.reject('失败1')
})
.then(res3=>{
console.log('res3--->', res3)
})
.catch((err:string)=>{
console.log('err--->',err )
})
3.2 链式编程-基本使用
// 链式编程回调地狱写法
const req = http.createHttp()
req.request('接口1')
.then(res1 => {
return req.request('接口2?p=接口1的结果res1')
})
.then(res2 => {
return req.request('接口3?p=接口2的结果res2')
})
.then(res3 => {
return req.request('接口4?p=接口3的结果res3')
})
.then(res4 => {
return req.request('接口5?p=接口4的结果res4')
})
.then(res5 => {
return req.request('接口6?p=接口5的结果res5')
})
.then(res6 => {
})
作用: Promise的链式编程是用来解决 回调函数地狱 的一种写法,让 多层嵌套的代码 变得可读性强
3.3 链式编程-实际应用
省市区案例
回调函数炼狱式写法
getList() {
req.request('https://hmajax.itheima.net/api/province')
.then(res => {
// 获取第一个省份的名字
let obj: iRes = JSON.parse(res.result.toString())
let pname = obj.list[0]
// 2. 刷新页面中的省份列的数据
this.list[0] = obj.list
// 获取市
req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent(pname)}`)
.then(res1 => {
let cityobj: iRes = JSON.parse(res1.result.toString())
this.list[1] = cityobj.list
// 获取区
req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(pname)}&cname=${encodeURIComponent(cityobj.list[0])}`)
.then(res2 => {
let areaobj: iRes = JSON.parse(res2.result.toString())
this.list[2] = areaobj.list
})
})
})
}
链式编程式写法
getList1() {
// 链式编程
// promse对象.then(()=>{ return p }).then(()=>{ return p1 }).....catch()
// 1. 获取请求省份的promise对象
req.request('https://hmajax.itheima.net/api/province')
.then(res => {
let pobj: iRes = JSON.parse(res.result.toString())
// 2. 刷新页面中的省份列的数据
this.list[0] = pobj.list
this.pname = pobj.list[0]
// 3. return一个市的接口请求Promise对象
return req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent(this.pname)}`)
})
.then(res1 => {
// 4. 获取市的相关数据
let cityobj: iRes = JSON.parse(res1.result.toString())
this.list[1] = cityobj.list
// 5. return一个区的接口请求Promise对象
return req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(this.pname)}&cname=${encodeURIComponent(cityobj.list[0])}`)
})
.then(res2 => {
// 6. 区的数据
let areaobj: iRes = JSON.parse(res2.result.toString())
this.list[2] = areaobj.list
})
.catch((err: string) => {
AlertDialog.show({ message: err })
})
}
4 async 函数
Promise 的链式编程虽然不用嵌套了,但是依旧有回调函数
我们可以使用 async 函数 结合 await来进一步优化,让代码变得简单和易读
Promise的链式编程写法
const req = http.createHttp()
req.request('https://hmajax.itheima.net/api/province')
.then(res=>{
let obj: iRes = JSON.parse(res.result.toString())
this.multi[0] = obj.list
return req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent('河北省')}`)
})
.then(res => {
let obj: iRes = JSON.parse(res.result.toString())
this.multi[1] = obj.list
return req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent('河北省')}&cname=${encodeURIComponent('石家庄市')}`)
})
.then(res => {
let obj: iRes = JSON.parse(res.result.toString())
this.multi[2] = obj.list
})
async和await结合优化
async getData() {
const req = http.createHttp()
let res1 = await req.request('https://hmajax.itheima.net/api/province')
let res2 = await req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent('河北省')}`)
let res3 = await req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent('河北省')}&cname=${encodeURIComponent('石家庄市')}`)
let prov:iRes = JSON.parse(res1.result.toString())
let city:iRes = JSON.parse(res2.result.toString())
let area:iRes = JSON.parse(res3.result.toString())
this.multi[0] = prov.list
this.multi[1] = city.list
this.multi[2] = area.list
}
4.1 async/await基础
4.1.1 async/await是什么?
async/await是一种用于处理异步操作的Promise语法糖。
通过使用async关键字声明一个函数为异步函数(返回值是Promise类型)
使用await关键字等待Promise的解析(完成或拒绝),以同步的方式编写异步操作的代码。
4.1.2 async/await基本语法
核心步骤:
- async 修饰函数
- await 等待成功(Promise 对象)
async function func() {
// await 获取到的是 之后 Promise 对象的成功结果
const res1 = await Promise对象1
const res2 = await Promise对象2
const res3 = await Promise对象3
}
//执行func函数
func()
举例
// 获取一个随机数,返回一个Promise对象
function getRandom() {
return new Promise<string>((resolve, reject) => {
let num = Math.floor(Math.random() * 100)
resolve(num.toString())
})
}
// 定义async函数func,里面使用await等待Promise对象返回结果
async function call() {
let res = await getRandom()
console.log(`res-->`, res)
let res1 = await getRandom()
console.log(`res-->`, res1)
let res3 = await 100
console.log(`res-->`, res3)
}
call()
4.2 async函数和await的特点
- async函数的特点
1.async修饰的函数默认返回的是Promise对象, 所以可以放到await关键字后面来调用
2.async修饰的函数内部return的数据需要使用 await 或者 .then() 或者.catch 来接收
- await的特点
1.await关键字必须放在async修饰的函数中才能使用
2.await关键字后面是一个Promise对象,如果是一个普通值会自动转为Promise对象来执行
3.await会等待其后面的Promise对象的成功执行状态,将结果值赋值给 = 号前面的变量
await 如果后面的函数执行时间很长,会卡住调用方法的执行 sync与await使用场景:不能使用在花费很长时间的异步处理上 这种情况请使用.then方式来处理
如果Promise是失败状态,则不会再往下继续执行
如果想要执行,需要使用try/catch来捕获错误
4.3 try/catch捕获错误
使用 async 之后需要try/catch 来捕获异常
注意: try-catch可以用来捕获任意的异常,并不仅仅局限于 async 函数
try/catch基本语法:
try {
// 需要被执行的语句
//一旦出错,就会触发catch中的代码执行
} catch (error) {
// error 接收错误信息
// 处理错误信息代码
}
举例
// 获取一个随机数,返回一个Promise对象
function randomNum() {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
const num = Math.floor(Math.random() * 100)
reject(num.toString())
}, 2000)
})
}
// 定义async函数func,里面使用await等待Promise对象返回结果
async function func() {
console.log('run--->begin')
//由于Promise对象的执行状态为拒绝(失败状态),则后面代码不会再执行
// 我们需要使用try/catch来捕获异常,让代码能够继续往下执行
try {
const num1 = await randomNum()
console.log('num1=', num1)
} catch (err) {
console.error('err--->', err)
}
console.log('run--->end')
}
func()
运行结果:
4.4. async/await改造省市区数据获取
import { http } from '@kit.NetworkKit'
const req = http.createHttp()
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
aboutToAppear(): void {
this.getlist()
}
async getlist() {
try {
let resProvince = await this.getPData()
let ProvinceObj: IResponse = JSON.parse(resProvince.result.toString())
this.range[0] = ProvinceObj.list
let resCity = await this.getCData(ProvinceObj.list[0])
let cityObj: IResponse = JSON.parse(resCity.result.toString())
this.range[1] = cityObj.list
let resArea = await this.getAData(ProvinceObj.list[0], cityObj.list[0])
let areaObj: IResponse = JSON.parse(resArea.result.toString())
this.range[2] = areaObj.list
} catch (err) {
AlertDialog.show({
message: err
})
}
}
async getPData() {
return req.request(`https://hmajax.itheima.net/api/province`)
}
async getCData(pname: string) {
return req.request(`https://hmajax.itheima.net/api/city?pname=${encodeURIComponent(pname)}`)
}
async getAData(pname: string, cname: string) {
return req.request(`https://hmajax.itheima.net/api/area?pname=${encodeURIComponent(pname)}&cname=${encodeURIComponent(cname)}`)
}
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,
})
.canLoop(false)
.onChange((value, index) => {
clearTimeout(this.timeID)
this.timeID = setTimeout(async () => {
if (value[0] != this.values[0]) { // 切换省
let resCity = await this.getCData(value[0])
let cityObj: IResponse = JSON.parse(resCity.result.toString())
this.range[1] = cityObj.list
let resArea = await this.getAData(value[0], cityObj.list[0])
let areaObj: IResponse = JSON.parse(resArea.result.toString())
this.range[2] = areaObj.list
this.selected = [this.range[0].indexOf(value[0]), 0, 0]
this.values = [value[0], cityObj.list[0], areaObj.list[0]]
} else if (value[0] == this.values[0] && value[1] != this.values[1]) { // 切换市
let resArea = await this.getAData(value[0], value[1])
let areaObj: IResponse = JSON.parse(resArea.result.toString())
this.range[2] = areaObj.list
this.selected = [this.range[0].indexOf(value[0]), this.range[1].indexOf(value[1]), 0]
this.values = [value[0], value[1], areaObj.list[0]]
}
this.values = value as string[]
}, 300)
})
}
}
}
运行截图
5 Promise 静态方法
在日常开发中除了使用 Promise 对象以外,还可以通过 Promise 提供的静态方法来管理多个异步
5.1 Promise.resolve
是什么: 返回一个成功原因的 Promise 对象
基本用法
Promise.resolve('成功原因')
.then(res => {
AlertDialog.show({ message: res })
})
5.2 Promise.reject
是什么: 返回一个拒绝原因的 Promise 对象
基本用法
Promise.reject('拒绝原因')
.catch((err: string) => {
AlertDialog.show({ message: err })
})
5.3 Promise.race
是什么: Promise.race()方法用于处理一组异步操作,并返回第一个完成的异步操作的结果
场景: 多个服务器提供相同的数据,但响应时间不同,你想要最快的那个服务器给你响应数据
基本用法
const p1 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('1')
}, 2000)
})
const p2 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject('2')
}, 1000)
})
// Promise.race方法作用:可以执行一组异步对象,返回第一个执行完成的异步对象的结果
Promise.race([p1, p2])
.then(res => {
console.log('race执行结果--->', res)
})
.catch((err:string)=>{
console.log('err race执行结果--->', err)
})
/*race方法,可以执行一组异步对象
注意点:最快的那个异步对象如果是成功状态,则使用.then来接受结果
否则使用.catch来接收结果
应用场景:多个服务器提供相同的数据,但响应时间不同,你想要最快的那个服务器给你响应数据
*/
案例
一次性获取北京,上海,广州三个城市的城市编码信息,把最快响应的哪个接口数据打印出来
import { http } from '@kit.NetworkKit'
async function func(){
const req = http.createHttp()
const p1 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('北京')}`)
const p2 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('上海')}`)
const p3 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('广州')}`)
// 结果是一个数组
const res = await Promise.race([p1, p2, p3])
// 打印
console.log(``, res.result)
}
func()
运行结果
通过多次运行打印可以看出哪个接口反应快,就会输出哪个接口的数据
5.4 Promise.all
是什么:
Promise.all方法用于处理多个Promise实例
如果所有Promise实例都成功完成,将所有成功的结果作为一个数组返回
如果任何一个Promise实例失败,返回第一个失败的Promise实例的原因
场景: 同时请求多个API
基本用法
const p1 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('1')
}, 2000)
})
const p2 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject('2')
}, 1000)
})
const p3 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject('3')
}, 1000)
})
Promise.all([p1, p2, p3]).then((res) => {
console.log('res:', res)
}, (err: string) => {
console.log('err:', err)
})
案例
一次性获取北京,上海,广州三个城市的城市编码信息,打印出来
import { http } from '@kit.NetworkKit'
async function func(){
const req = http.createHttp()
const p1 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('北京')}`)
const p2 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('上海')}`)
const p3 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('广州')}`)
// 结果是一个数组
const res = await Promise.all([p1, p2, p3])
// 打印
console.log(``, JSON.stringify(res[0].result))
console.log(``, JSON.stringify(res[1].result))
console.log(``, JSON.stringify(res[2].result))
}
func()
运行结果
5.5 Promise.allSettled
是什么:
allSettled 可以获取所有的结果,无论成功失败
基本用法
const p1 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('1')
}, 2000)
})
const p2 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject('2')
}, 1000)
})
Promise.allSettled([p1, p2, 'itheima'])
.then((res) => {
console.log('res:', JSON.stringify( res))
// [{"status":"fulfilled","value":"1"},{"status":"rejected","reason":"2"},{"status":"fulfilled","value":"itheima"}]
}, (err: string) => {
console.log('err:', err)
})
案例
一次性获取北京,上海,广州三个城市的城市编码信息,打印出来
async function func(){
const req = http.createHttp()
const p1 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('北京')}`)
const p2 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('上海')}`)
const p3 = req.request(`https://hmajax.itheima.net/api/weather/city?city=${encodeURIComponent('广州')}`)
// 结果是一个数组
const res = await Promise.allSettled([p1, p2, p3])
// 打印
console.log(``, JSON.stringify(res[0]))
console.log(``, JSON.stringify(res[1]))
console.log(``, JSON.stringify(res[2]))
}
运行结果