Promise的介绍与在鸿蒙中的使用
异步编程
学习Promise的开始,我们要先了解异步编程
一般代码的执行是单线程的机制,就是按次序执行,执行完一个任务后,再执行下一个,如果我们在页面加载的同时时候执行一个请求,拿到数据后映射到界面上,这时我们就需要异步操作来执行这个请求
异步就相当于从主线程发射一个子线程来完成任务,每一个任务有一个或者多个
回调函数
,前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务不用等前一个任务结束就会执行
所以程序执行顺序与任务的排列顺序是不一致的,异步的
回调函数
刚刚我们提到了回调函数
回调函数的定义非常简单:一个函数被当做一个实参传入到另一个函数(外部函数)
,并且这个函数在外部函数内被调用,用来完成某些任务的函数。就称为回调函数
回调函数的两种写法(实现效果相同):
- 将回调函数写在外边
const text = () => {console.log('执行代码');}setTimeout(text, 1000)
- 直接将回调函数写成参数
setTimeout(()=>{console.log('执行代码');}, 1000)
这段代码中的 setTimeout 就是一个消耗时间较长的过程,它的第一个参数是个回调函数,第二个参数是毫秒数,这个函数执行之后会产生一个子线程,子线程会等待 1 秒,然后执行回调函数 “text”,在文本中输出执行代码
setTimeout会在子线程中等待1秒,但是主线程的运行不会受到影响!例如以下代码
setTimeout(()=>{console.log('执行代码');}, 1000)console.log('123456');
在这里会先打印出来123456(主线程),然后一秒后在文本中输出执行代码(子线程)
回调地狱
在学习Promise之前我们需要先了解什么是回调地狱以及产生回调地狱的原因
概念,当一个回调函数嵌套一个回调函数的时候就会出现一个嵌套结构,当嵌套的数量足够多的时候就会出现回调地狱的情况.
举个例子
比如我们需要发送三个ajax请求
- 第一个正常请求
- 第二个需要第一个请求的返回值作为参数
- 第三个需要第二个请求的返回值作为参数
$.ajax({url: '我是第一个请求',type: 'get',success (res) {// 现在发送第二个请求$.ajax({url: '我是第二个请求',type:'post',data: { a: res.a, b: res.b },success (res1) {// 进行第三个请求$.ajax({url: '我是第三个请求',type:'post',data: { a: res1.a, b: res1.b },success (res2) { console.log(res2) }})}})}
})
不用解释就可以看出来当前代码的
可维护性差
的特点,所以这时就需要需要我们的Promise去解决回调地狱的问题.
Promise(承诺)
Promise是一种用于处理异步操作的对象,可以将异步操作转换为类似于同步操作的风格,以方便代码编写和维护。
Promise提供了一个状态机制来管理异步操作的不同阶段,并提供了一些方法来注册回调函数以处理异步操作的成功或失败的结果。
Promise有三种状态:pending(进行中)、fulfilled(已完成)和rejected(已拒绝)。Promise对象创建后处于pending状态,并在异步操作完成后转换为fulfilled或rejected状态。
- 只有异步操作的结果可以决定是哪一种状态,其他任何操作都无法改编这个状态(承诺)
- Promise对象的状态改变,只有两种可能:
从pending变为fulfilled和从pending变为rejected
。- 只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
无论何时调用,只要执行结束就可以一直得到这个结果
最基本的用法是通过构造函数实例化一个Promise对象,同时传入一个带有两个参数的函数,通常称为executor函数。
executor函数接收两个参数:resolve和reject,分别表示异步操作成功和失败时的回调函数。
目的就是更优雅的书写复杂的
异步任务
语法格式
首次创建一个Promise对象,并将其作为一个方法的返回值
// 在test方法中设定返回值为Promise对象,为其传入一个返回的类型,就比如说number
async test(): Promise<number> {return new Promise((resolve, reject) => {//业务逻辑代码,你想要执行异步任务的代码if (...){//如果进入你设计的代码逻辑,则执行resolve回调函数并将执行数字作为参数传递;resolve(0)}else{//否则执行reject回调函数并传递一个错误对象作为参数。reject(new Error('Random number is too small'))}})
}
在另外一个地方进行当前方法的调用
this.test().then((res:number)=>{console.info(`number is ${res}`);
}).catch((error:BusinessError)=>{console.error(error.message);
})
上述代码中,then方法的回调函数接收Promise对象的成功结果作为参数,并将其输出到控制台上。如果Promise对象进入rejected状态,则catch方法的回调函数接收错误对象作为参数,并将其输出到控制台上。
如果你想表达的是成功回调,你可以在内部调用函数
reslove('一般情况下是后端返回的成功数据)
。如果你想表达的是失败回调,你可以调用reject('一般情况下是后端返回的失败信息')
.
Promise链式调用
then方法返回的是一个新的Promise实例(注意:不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法
- 实际案例:
我想要实现在一个数组中查看一个帖子,但是我最终的目的是得到这个帖子下面的所有评论,这该怎么实现呢? - 实现思路:
先从一个接口中获取这个帖子的信息,然后通过该帖子的帖子id从而获取到该帖子下的所有评论
pajax({url:"http://localhost:3000/news",data : {author : "james"}
}).then(res=>{return pajax({url : "http://localhost:3000/comments",data : {newsId : res[0].id}})
}).then(res=>{console.log(res);
}).catch(err=>{console.log(err);
})
这样也就解决了上边我们所提到的回调地狱的问题,
使用Promise链式调用
,
Promise.all()
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
。
- 语法格式
// Promise.all一次性处理多个异步请求返回数据Promise.all([promise1, promise2,promise3, promise4]).then(res => {this.flag1 = res[0];this.flag2 = res[1];this.flag3 = res[2];this.flag4 = res[3];})
Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例
,如果不是,就会调用Promise.reslove() [该方法可自行了解]自动将参数转为 Promise 实例,再进一步处理。
说那么多白话没用,我们可以根据一个案例,就可以明白Promise.all()的用途了。
- 实际案例
如果你想实现一个效果:在一个页面中,等到页面中所有的请求返回数据后,再渲染页面,该怎么实现呢?(在实际开发中我们会看到loading
加载页面,等数据返回完后,loading加载页面会消失,整个页面就展现出来了,增强用户的体验。)
// aboutToAppear函数在创建自定义组件的新实例后,在执行其build()函数之前执行。aboutToAppear(): void {// Promise.all一次性处理多个异步请求返回数据Promise.all([promise1, promise2,promise3, promise4]).then(res => {this.flag1 = res[0];this.flag2 = res[1];this.flag3 = res[2];this.flag4 = res[3];})}build() {if (this.flag1 != -1) {//组件1}if (this.flag2 != -1) {//组件2}if (this.flag3 != -1) {//组件3}if (this.flag4 != -1) {//组件4}}
执行aboutToAppear()方法在页面执行前获取数据,获取到数据后,再通过flag控制组件的显示.
有关aboutToAppear()方法具体使用方法,请了解鸿蒙应用具体生命周期.
总结
虽然解决异步编程的终极解决方案是async和await
,但是它们也是基于Promise封装而来的,不过了解Promise也是很重要的