我们在上篇文章提到了异步会导致无法通过返回值来获取函数的执行结果,我们通过传入一个回调函数的方式,以参数的形式获取到了我们想要获取的数据,但是这样如果需要对数据进行多次操作导致形成回调地狱那种不便于阅读以及护理的代码。
为了解决这个问题Promise(承诺)作为一个存储数据的容器,帮助我们解决需要用回调函数的参数获取异步函数执行结果的问题,拥有着一套特殊的存储数据的方式。
1.Promise的介绍
首先就是创建Promise,我们要认识到Promise是一个构造函数,模拟类,通过new去实例化,Promise是内建的构造函数,本质上就是类,用new得到实例(承诺对象)
Promise构造函数需要一个函数作为参数,Promise构造函数的回调函数会在创建Promiss时调用,同时会有两个参数传递进去,resolve和reject两个函数,通过这两个函数可以向Promise存储数据,resolve在执行正常的时候存储数据 reject在执行错误的时候存储数据。
当我们想要从Promise中读取数据的时候,要通过Promise实例方法then去读取Promise中存储的数据,then需要两个回调函数作为参数,resolve存储的数据会在第一个函数中作为参数返回,reject存储的数据或者丢出的错误会在第二个函数作为参数返回。
代码如下。
Promise还隐藏了两个隐藏属性 PromiseReault存储数据还有PromiseState记录Promise的状态(三种状态 fulfilled(完成) rejected(出错拒绝) pending(正在进行中)),state只能修改一次,修改以后永远不会更改,state是私有的 reject和reslove只能运行一个。
Promise的运行流程---
当Promise创建的时候,PromiseState初始值是pending,当resolve存储数据时,PromiseState变为fulfilled PromiseResult存储数据。
当reject存储数据时,PromiseState变为rejected PromiseResult存储数据或者异常对象。
当我们通过then读取数据的时候,相当于为Promise设置了回调函数,如果PromiseState变为fulfilled则调用then第一个回调函数返回数据 如果是reject则调用第二个回调函数返回数据。
catch和then类似,catch()相当于then(null,(reason)=>{})专门处理异常的方法
finally()也是传一个回调函数,无论正常存储数据还是出现异常都会执行。一般用来编写无论成功与否都需要执行的代码逻辑。
2.Promise的详情
我们现在会使用Promise之后,搞一下原理.Promise就是一个存储数据的对象,但是存储方式特殊,所以可以直接将异步调用的结果存储到Promise中。
const Promise = new Promise((resolve,reject)=>{})
这句代码中Promise里面的函数是我们传进去的,resolve和reject是作为参数给我们返回的。所以我们定义这个函数就是为了让Promise把resolbe和reject这两个函数传过来。然后resolve和reject存储数据。
我们现在回归正题,用promise去解决我们的回调地狱问题。
function sum(a,b,cb){setTimeout(()=>{cb(a+b)},1000)
}
const result=sum(1,1,(result)=>{sum(result,1,(result)=>{sum(result,1,(result)=>{sum(result,1,(result)=>{console.log(result);})})})
})
这是我们最开始没有Promise的回调地狱。
我们现在希望通过Promise去改进代码。但是这时候我们发现我们好像欠缺了点东西。我们虽然存储数据了但是我们想要实现多次调用还是需要一直调用
这好像也是回调函数。
现在添加一个新知识点。Promise中的then和catch方法的返回值都会存储到新的Promise中。这样我们就知道如何去避免回调地狱了。
function sum1(a,b){return new Promise((resolve,reject)=>{setTimeout(()=>{resolve(a+b)},1000)})
}
const p1 =sum1(1,1).then((result)=>{return '楚河日当午'
})
const p3 = p1.then((result)=>{console.log(result);return 'gogogo'
})
p3.then((result)=>{console.log(result);
})
setTimeout(()=>{console.log('p1',p1)
},1000)//
我们可以第一个then返回一些数据然后赋值给一个变量,我们记得返回的是一个new Promise
然后PromiseResult就是我们的返回值,我们就可以再次通过then去获取最新的值,就可以实现每次then都可以获取到最新的值。
这样就解决了我们的回调地狱这个问题,现在我们来看我们的catch方法。
我们发现在我们对Promise实例进行链式调用的时候,后面的方法thencatch读取上一步的执行结果如果上一步的执行结果不是我们当前想要的,就会跳过当前的方法。
还有如果我们丢出错误没有用catch捕获就会报错。(Promise调用链没有catch却扔出错误)
Promise是甩锅对象,如果catch出错了会直接抛出错误,需要用catch捕获。通常情况下catch写最后面就不用这样连续用了。相当于保安
这里是一些扩展的方法以及使用方式。不常用。
这就是Promise的使用了,当然想要深度理解还需要去分清楚异步的执行顺序宏任务和微任务,以及手写Promise。如果仅仅是知道怎么去用,这就已经足够了