看完这篇blog你要是还不能理解Promise那就.........

    科技2022-08-17  103

    Promise

    1. 什么是Promise呢?

    第一次接触promise的时候一脸蒙不知道这个东西是什么?是干什么的? 包括看了官方文档后还是不太清楚

    2. Promise到底是做什么的呢?

    其实是一种异步编程的解决方案

    3. 那我们什么时候能用到它呢?
    网络请求我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的1+1=2一样将结果返回。所以往往我们会传入另外一个函数,在数据请求成功时,将输入通过传入的函数回调出去。如果知识一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。但是,当网络请求非常复杂时,就会出现回调地狱。

    什么?回调地狱是什么?完全没听过啊!不要着急 接着往下看

    我们来考虑下面这样的场景

    我们需要通过一个url1从服务器加载一个属于data1,data1中包含了下一个需要请求的url2而我们就需要通过data1中取出url2,在从服务器加载data2,此时data2中包含了下一个请求的url3这是我们通过data2取出url3,从服务器加载数据data3,而data3中包含了下一个请求的url4发送网路请求 请求url4, 获取最终的数据data4有可能还更多而这种情况我们就称之为 回调地狱 还没太看明白,那我们就看下面的伪代码吧:(方便起见用了jquery的ajax) $.ajax('url1', function (data1) { $.ajax('url2', function (data2) { $.ajax('url3', function (data3) { $.ajax('url4', function (data4) { console.log(data4); }) }) }) })

    看起来简单,但是其中还包含的对每次请求到的data进行解析的过程,代码中没有标注出来。

    虽然这样写没什么问题,但是,这样的代码比较复杂而且不容易维护。我们更加期望的是一种优雅的方式来进行这种异步操作。

    这就要用到Promise,它可以以一种非常优雅的方式来解决这个问题 。

    4. 这里我们先以异步操作setTimeout为例,来感受下Promise的魅力:
    new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello World') }) }).then((data) => { console.log(data); })

    简而言之就是将请求来的数据放在.then()中操作。

    一般情况下是由异步操作时,使用Promise对这个异步操作进行封装。

    在resolve中接受data,在.then中可调用,上面这段代码可以直接把Hello World打印出来,也就印证了resolve中传递data给.then的作用了。

    Promise中的resolve 和 reject都是js内部定义的函数,resolve 成功时调用。reject 失败时调用,而此时一旦调用reject就会把错误信息返回到.catch中:

    new Promise((resolve, reject) => { setTimeout(() => { //resolve('Hello World') reject('Error') }) }).then((data) => { console.log(data); }).catch(err => { console.log(err) })

    这里也成功的将Error Message打印了出来

    5. Promise的三种状态,其实上一条已经表示的差不多了,这里再总结一下:

    首先,当我么你再开发中有异步操作时,就可以给异步操作包装一个Promise 而异步操作之后会有三种状态:

    pendding:等待状态,比如正在进行网络请求,或者定时器没有到时间。fufill:(也称为resolved)满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()reject:拒绝状态,当我们主动毁掉了reject时,就处于该状态,并且会回调.catch()

    Promise还有另一种写法,直接再.then中传入两个函数的参数,第一个是resolve的结果,第二个就是reject的结果:

    new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello World') // reject('error message') }, 1000) }).then(data => { console.log(data); //Hello World },err => { console.log(err); }) new Promise((resolve, reject) => { setTimeout(() => { //resolve('Hello World') reject('error message') }, 1000) }).then(data => { console.log(data); },err => { console.log(err);// error message })

    都如愿以偿的打印出了想要的结果

    6. Promise的链式调用

    我们来想象一下有这样一个需求: 1. 我们第一次进行网络请求拿到了数据‘aaa’,2. 我们想给拿到的aaa拼接字符串‘111’,然后再给拼接后的字符串再拼接’222‘: (这里我们假设setTimeout就是一次请求)

    new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000) }).then(res => { // console.log(res, '第一层处理的10行代码') //对结果进行第一次处理 return new Promise((resolve) => { //reject可以不传 resolve(res + '111'); }) }).then(res => { console.log(res, '第二层处理的10行代码') return new Promise((ressolve) => { resolve(res + '222') }) }).then(res => { console.log(res, '第三层的10行处理代码') })

    上面代码就是一个很优雅的链式调用。下面是打印的结果 其实上面的代码还可以简写:

    new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000) }).then(res => { console.log(res, '第一层处理的10行代码') //对结果进行第一次处理 return Promise.resolve(res + '111') }).then(res => { console.log(res, '第二层处理的10行代码') return Promise.resolve(res + '222') }).then(res => { console.log(res, '第三层的10行处理代码') })

    打印结果都是一样的,但其实还可以再简化,省略掉Promise.resolve:

    new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000) }).then(res => { console.log(res, '第一层处理的10行代码') //对结果进行第一次处理 return res + '111' //内部会对Promise进行包装,并且内部调用resolve }).then(res => { console.log(res, '第二层处理的10行代码') return res + '222' }).then(res => { console.log(res, '第三层的10行处理代码') })

    结果还是能正确打印。但如果中间某一步调用的时reject的时候,那么下面的代码就不会执行,直到.catch();

    new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000) }).then(res => { // console.log(res, '第一层处理的10行代码') //对结果进行第一次处理 return new Promise((resolve, reject) => { //reject可以不传 // resolve(res + '111'); reject('error 1') }) }).then(res => { console.log(res, '第二层处理的10行代码') return new Promise((ressolve) => { resolve(res + '222') }) }).then(res => { console.log(res, '第三层的10行处理代码') }).catch(err => { console.log(err); })

    我们再来看看打印结果: 而reject和resolve一样也有简写return Promise.reject('error 1');

    7.Promise.all()

    如果某个需求需要发送两次需求才能继续执行,我们可以使用Promise.all()方法。我们还是以setTimeout为例,直接就把setTimeout当作网络请求:

    Promise.all([ new Promise((resolve, reject) => { setTimeout(() => { resolve({age: 18, name: 'Venciki'}) }, 2000) }), new Promise((resolve, reject) => { setTimeout(() => { resolve({age: 19, name: 'Relo'}) }, 1000) }) ]).then(results => { console.log(results) })

    上面代码中发送了两次请求(使用了两次setTimeout)我们把他门放在all方法中,最终可以通过一个.then(results)的results来接受两次请求的返回的参数:

    Processed: 0.017, SQL: 9