Promise是什么?
Promise 是用来管理异步操作的对象,可以获取成功(或失败)的结果,代表了一个尚未完成的异步操作,最终会以成功(fulfilled)或失败(rejected)的状态结束。
Promise 的设计目的是为了解决回调地狱(多层回调函数嵌套)的问题,使得异步流程更加清晰和易于管理。
下面是两种方式的对比:
代码示例ArkTs语法:
// 回调地狱写法
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优化后的写法
// 链式编程回调地狱写法
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 的几个关键点:
1. 状态:
Pending(待定):初始状态,既不是成功也不是失败。
Fulfilled(已兑现):异步操作成功完成。
Rejected(已拒绝):异步操作失败。
2. 方法:
then:用于注册回调函数。当 Promise 处于 fulfilled 状态时,第一个回调函数(通常称为 onFulfilled)会被调用;当 Promise 处于 rejected 状态时,第二个回调函数(通常称为 onRejected)会被调用。
catch:用于注册一个回调函数处理错误,这个回调函数会在 Promise 被 reject 时被调用。
finally:无论结果是 fulfilled 还是 rejected,都会被执行。(用的较少)
Promise 的静态方法
resolve 和 reject 这两个在封装的时候见得多一些,不需要实例化,直接就可以获取 成功 或者 拒绝 的 Promise 对象
Promise.resolve
//返回一个成功原因的 Promise 对象
Promise.resolve('成功原因')
.then(res => {
AlertDialog.show({ message: res })
})
Promise.reject
//返回一个拒绝原因的 Promise 对象
Promise.reject('拒绝原因')
.catch((err: string) => {
AlertDialog.show({ message: err })
})
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)// 输出:err race执行结果---> 2(因为1000毫秒先执行完)
})
/*race方法,可以执行一组异步对象
注意点:最快的那个异步对象如果是成功状态,则使用.then来接受结果
否则使用.catch来接收结果
应用场景:多个服务器提供相同的数据,但响应时间不同,你想要最快的那个服务器给你响应数据
*/
Promise.all
是什么:Promise.all方法用于处理多个Promise实例,如果所有Promise实例都成功完成,将所有成功的结果作为一个数组返回,如果任何一个Promise实例失败,返回第一个失败的Promise实例的原因
场景:同时请求多个API
@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%')
}
}
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)
})
3. 特点:
不可变性:一旦一个Promise 的状态被确定(fulfilled 或 rejected),就不能再改变。
调用 resolve 之后再调用 reject,状态还是 已兑现,反之亦然
链式调用:then 方法返回一个新的 Promise,可以链式调用,这样可以连续处理多个异步操作。
在 Promise的链式编程写法中,有如下2 个特性,需要了解:
- Promise 的 then 方法会自动返回一个新Promise 对象
- then 方法的返回值会影响这个 新Promise对象的结果是已兑现状态还是已拒绝状态
链式编程写出来的代码结构大概是这样子,then 和 then 以及 catch 之间是平级的
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 )
})
4. 用途:
异步操作的抽象:Promise 抽象了异步操作的过程,使得开发者可以以同步的方式编写异步代码。
错误处理:通过catch 方法可以集中处理异步操作中的错误。
组合异步操作:通过Promise.all 和 Promise.race 等方法可以方便地组合多个异步操作。
// 异步操作的抽象:Promise 抽象了异步操作的过程,使得开发者可以以同步的方式编写异步代码
const asyncOperation = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('异步操作完成');
}, 1000);
});
asyncOperation
.then((result) => {
console.log(result); // 输出:异步操作完成
});
// 错误处理:通过catch 方法可以集中处理异步操作中的错误
const errorProneOperation = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject('发生错误');
}, 1000);
});
errorProneOperation
.then((result) => {
console.log(result);
})
.catch((error:string) => {
console.error(error); // 输出:发生错误
});
// 组合异步操作:通过Promise.all 和 Promise.race 等方法可以方便地组合多个异步操作
const operation1 = new Promise<string>((resolve) => {
setTimeout(() => {
resolve('操作1完成');
}, 1000);
});
const operation2 = new Promise<string>((resolve) => {
setTimeout(() => {
resolve('操作2完成');
}, 2000);
});
// 使用Promise.all组合多个异步操作
Promise.all([operation1, operation2])
.then((results) => {
console.log(results.toString()); // 输出:['操作1完成', '操作2完成']
});
// 使用Promise.race选择最快完成的异步操作
Promise.race([operation1, operation2])
.then((result) => {
console.log(result); // 输出:操作1完成
});
补充:async函数和 await
(待补充)
总结
Promise 的引入极大地简化了异步编程模型,并且使得异步代码的编写更加优雅和易于理解。在现代 Web 开发中,Promise 已经成为处理异步操作的标准方式之一。