Promise
1.1什么是promise
1.Promise 是用来管理异步操作的对象,可以获取成功(或失败)的结果
核心代码:
const p = new Promise<string>((resolve, reject) => {
// 执行任意代码,主要是异步
// 成功 resolve(成功结果) then 执行
// 失败 reject(失败结果) then第二个回调函数或 catch 执行
})
p.then(res => {}, (err: string) => {})
// 或者
p.then(res=>{}).catch((err:string)=>{})
2.作用: 能够解决回调函数地狱(多层回调函数嵌套)
3.如何用:
用 Promise 来管理异步任伍分 3 步走:
-
内部执行异步代码
-
传递成功结果
-
传递失败结果
//内部执行异步代码
// 实例化 Promise 对象
const p = new Promise<string>(() => {
// 执行任意代码,主要是异步
setTimeout(() => {
// 比如:获取随机数
const randomNum = Math.floor(Math.random() * 100)
console.log('随机数是:', randomNum)
},2000)
})
}
//2传递成功结果
const p = new Promise<string>((resolve) => {
// 执行任意代码,主要是异步
setTimeout(() => {
// 比如:获取随机数
const randomNum = Math.floor(Math.random() * 100)
resolve(randomNum.toString())
},2000)
})
p.then(res => {
console.log('res:', res)
})
//返回失败结果
const p = new Promise<string>((resolve,reject) => {
// 执行任意代码,主要是异步
setTimeout(() => {
// 比如:获取随机数
const randomNum = Math.floor(Math.random() * 100)
// resolve(randomNum.toString())
reject(randomNum.toString())
},2000)
})
p.then(res => {
console.log('res:', res)
})
.catch((err:string)=>{
console.log('err:',err)
})
再来看看http模块的 request 方法,他返回的就是一个Promise对象所以我们用.then和.catch接收的结果
1.2promise的状态
Promise 必然处于 3 种状态中的某一种,调用resolve,reject 的本质就是更改他的状态
3 种状态:
- 待定(pending): 初始状态,既没有被兑现(resolve),也没有被拒绝(reject)
- 已兑现(fullfilled): 意味着操作成功完成
- 已拒绝(rejected): 意味着操作失败
注意:状态的改变是不可逆的
调用 resolve 之后再调用 reject,状态还是 已兑现,反之亦然
直接得到一个成功或者失败的结果写法:
- Promise.resolve(成功信息) 直接得到一个已兑现(fullfilled)的Promise对象
- Promise.reject(失败信息)直接得到一个已拒绝(rejected)的Promise对象
不可逆测试
const p = new Promise<string>((resolve, reject) => {
resolve('success')
reject('err')
})
p
.then(res => {
console.log('res:', res)
})
.catch((err: string) => {
console.log('err:', err)
})
1.3链式编程的使用
作用:前面说过Promise的链式编程是用来解决【回调函数地狱】的一种写法,让多层嵌套的代码变得可读性强
让我们先看看回调地狱的写法:代码结构整体呈现三角行随着我们每一阶段业务代码的处理逻辑增加代码的可读性就如同地狱一般难以分析。
// 回调地狱写法
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=>{
})
})
})
})
})
})
那么链式编程的写法呢:可以让每一次的业务处理逻辑都处在同一级使可读性大大滴提升,注意:
- Promise 的 then 方法会自动返回一个新Promise 对象
- then 方法的返回值会影响这个 新Promise对象的结果是已兑现状态还是已拒绝状态
// 链式编程回调地狱写法
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 => {
})
链式编程的逻辑如下图所示
最后简单的总结一下链式编程的特点吧:copy下来看看结果
let p = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('成功1')
}, 10)
})
// 链式编程
/*
* 特点:
* 1. 后面的 .then是可以接受到前面.then返回的成功状态下的值
* 2. 后面的.catch是可以接受前面.then中返回的失败状态下的值
* 3. 一旦在某个.then里面返回的是一个失败状态的Pormise,则直接跳过其他
* 的.then进入到.catch执行
*
* 总结:
* 写法: p.then().then()....catch()
* 注意点:如果 后一个.then需要用到前一个.then中的结果,需要在前一个
* .then中 显示return一下
* */
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 )
})
@Entry
@Component
struct Index {
build() {
Column() {
}
.height('100%')
.width('100%')
.backgroundColor(Color.Pink)
}
}
async 函数函数和await
2.1 async/await基础
先来看看async函数和 await能够做什么?
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
}
可以看到代码更加简洁清晰
async/await是什么?
async/await是一种用于处理异步操作的Promise语法糖。
通过使用async关键字声明一个函数为异步函数(返回值是Promise类型)
使用await关键字等待Promise的解析(完成或拒绝),以同步的方式编写异步操作的代码。
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 randomNum() {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
const num = Math.floor(Math.random() * 100)
resolve(num.toString())
}, 10)
})
}
// 定义async函数func,里面使用await等待Promise对象返回结果
async function func() {
const num1 = await randomNum()
console.log(num1)
}
2.2 async函数和await的特点
如果Promise是失败状态,则不会再往下继续执行
如果想要执行,需要使用try/catch来捕获错误
- async修饰的函数默认返回的是Promise对象,所以可以放到await关键字后面来调用
- async修饰的函数内部return的数据需要使用 await 或者 .then() 或者.catch 来接收
- await关键字必须放在async修饰的函数中才能使用
- await关键字后面是一个Promise对象,如果是一个普通值会自动转为Promise对象来执行
- await会等待其后面的Promise对象的成功执行状态,将结果值赋值给 = 号前面的变量
//结合1,2观察以下函数
//1. 准备一个Promise对象
async function getRandom() {
let num = Math.floor(Math.random() * 100)
return num.toString() // 本质上是返回了一个Promise.Resolve(num.toString())
}
// 2. 使用一个异步函数来调用,
async function call() {
let res = await getRandom()
console.log('成功随机数:--->', res)
let res1 = await getRandom()
console.log('成功随机数:--->', res1)
}
再来看看await函数的特点
// 1. 准备一个Promise对象
function getRandom() {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
let num = Math.floor(Math.random() * 100)
// resolve(num.toString())
reject(num.toString())
}, 2000)
})
}
// 2. 使用一个异步函数来调用
async function call() {
let res = await getRandom()
console.log('成功随机数:--->', res)
let res1 = await getRandom()
console.log('成功随机数:--->', res1)
}
注意:✨✨✨✨✨
1.await关键字必须放在async修饰的函数中才能使用 (await不能单独使用,async可以单独使用)
2. await关键字后面是一个Promise对象,如果是一个普通值会自动转为Promise对象来执行
3. await会等待其后面的Promise对象的成功执行状态(不要在需要花费较长时间的异步函数中使用await)
2.3try/catch捕获错误
基本语法:将要执行的代码放入try中
try {
// 需要被执行的语句
//一旦出错,就会触发catch中的代码执行
} catch (error) {
// error 接收错误信息
// 处理错误信息代码
}
看看案例:
// 获取一个随机数,返回一个Promise对象
function randomNum(time:number) {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
const num = Math.floor(Math.random() * 100)
reject(num.toString())
}, time)
})
}
// 定义async函数func,里面使用await等待Promise对象返回结果
async function func() {
console.log('run--->begin')
//由于Promise对象的执行状态为拒绝(失败状态),则后面代码不会再执行
// 我们需要使用try/catch来捕获异常,让代码能够继续往下执行
try {
const num1 = await randomNum(300)
console.log('num1=', num1)
} catch (err) {
console.error('err--->',JSON.stringify(err))
}
console.log('run--->end')
}
Promise的静态函数
3.1. Promise.resolve
//返回一个成功发promise对象
//语法:
Promise.resolve('成功原因')
.then(res => {
AlertDialog.show({ message: res })
})
3.2. Promise.reject
/*返回一个拒绝原因的 Promise 对象
语法:
*/
Promise.reject('拒绝原因')
.catch((err: string) => {
AlertDialog.show({ message: err })
})
3.3. Promise.race
上面2个相信静态方法相信已经相等熟悉了 。
是什么: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来接收结果
应用场景:多个服务器提供相同的数据,但响应时间不同,你想要最快的那个服务器给你响应数据
*/
3.4. Promise.all
是什么:Promise.all方法用于处理多个Promise实例,如果所有Promise实例都成功完成,将所有成功的结果作为一个数组返回,如果任何一个Promise实例失败,返回第一个失败的Promise实例的原因
使用场景:(多个数据要么都必须有要么差一个就都不需要)
直接看代码跑跑和它的描述做一下对比:
@Entry
@Component
struct Index {
build() {
Column() {
Button('Promise.all基本语法')
.onClick(() => {
/*
* 总结:
* Promise.all的作用是?
* 执行一组p对象,如果所有成功则返回所有的执行结果(数组类型)
* .them() 接收成功的结果
* 如果有一个执行失败,直接返回失败的数据 (.catch来接收)
*
* 缺点:只要有一个失败了,其他的都拿不到数据
* 场景:多个请求要么同时拿到成功的数据,要么一个不要
* */
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(() => {
resolve('3')
}, 1000)
})
Promise.all([p1, p2, p3])
.then(res => {
AlertDialog.show({ message: JSON.stringify(res, null, 2) })
})
.catch((err: string) => {
AlertDialog.show({ message: '错误:' + err })
})
})
}
.height('100%')
.width('100%')
}
}
3.5. Promise.allSettled
相比于 all 方法,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])
.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)
})