Promise链式调用与async函数解决回调地狱问题

Promise链式调用与async函数解决回调地狱问题

在平时我们使用vue的时候经常会看到在请求接口的时候总是会有async与await:
请添加图片描述这是为什么呢?不能直接请求接口吗?

这里其实是为了避免出现回调地狱的问题。要理解清楚这个问题不妨从promise的链式调用开始讲起。

什么是回调地狱?

概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调地狱。

我们知道一个axios函数中包含一些url等请求参数和一个.then的回调函数,由于其异步性,一个接口的请求需要等到回调函数成功调回才算结束。于是要是我们在.then里面在嵌入一个axios函数的话就需要等到上一层请求后再进入这层,再由这层回调到上层。代码量一多,不仅可读性差而且异常无法捕获,耦合性严重,牵一发动全身。比如看下面代码:

axios({url:'http://hmajax.itheima.net/api/province'}).then(res => {
    const pname = res.data.list[5]
    console.log(pname);
    axios({url:'http://hmajax.itheima.net/api/city!', params: { pname }}).then(res => {
        const cname = res.data.list[0]
        console.log(cname);
    })        
}).catch(error => {
    console.log(error);
})

我在第二个url中最后加了一个“!”让其错误,并让寻找错误的地方。然而我们发现:

请添加图片描述

其错误直接来源于axios的源码处,而且显示Uncaught (in promise),这就是回调地狱最直接的一个弊端处。

好了这里又涉及到一个词叫promise。

何为Promise?

mdn的解释是:“Promise是一个对象,它代表了一个异步操作的最终完成或者失败。”所以说Promise就是一个函数返回的对象,不然每次做回调操作都要自己手动传一个回调函数进去。

请添加图片描述

解释:依靠then()方法回返回一个新生成的Promise对象特性,继续串联下一环任务,直到结束。而then()回调函数中的返回值会影响新生成的Promise对象最终状态和结果。这样通过链式调用,可以有效解决回调函数嵌套问题。

const p = new Promise((reslove, reject) => {
    setTimeout(() => {  //设置时间模拟ajax请求
        reslove('北京市')
    }, 2000)
})
const p2 = p.then(res => {
    console.log(res); //北京
})
console.log(p2 === p); //false

可以看到我们创建了一个新的promise对象并赋值给p,然后再用p触发回调函数,并生成了一个全新的promise对象并赋值给p2。

在最后一行的log中,使用了三等判断进行比较,我们知道三等判断,判断的是两个变量保存的内存地址,最后其返回了false更可以说明生成的是一个全新的promise对象。

所以知道了promise的工作原理,我们就可以这样写:

axios({url:'http://hmajax.itheima.net/api/province'}).then(res => {
    const pname = res.data.list[5]
    console.log(pname);
    return axios({url:'http://hmajax.itheima.net/api/city!', params: { pname }}).then(res => {
        const cname = res.data.list[0]
        console.log(cname);
    })        
}).catch(error => {
    console.log(error);
})

没错,就是在第二个axios前面多了一个return,就可以捕获到第二层的错误了。

请添加图片描述

async函数和await

我们现在知道了promise可以解决回调地狱,但是这还是在回调函数里面不断嵌套,可读性极差,这时async函数(意思为“异步”)的引入就极大的改善了这个问题。

定义:async函数是使用async关机字声明的函数。async函数是AsyncFunction构造函数的实例,并且其中允许使用await关键词。async和await关键词让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。

因此我们只需要这样:

const getData = async () => {
    const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province'})
    const pname = pObj.data.list[5]
    const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname }})
    const cname = cObj.data.list[0]
    console.log(pname);
    console.log(cname);
}
getData() //别忘了调用函数

或者:

let pname = []
let cname = []
const getData = async () => {
    await axios({ url: 'http://hmajax.itheima.net/api/province'}).then(res => {
        pname = res.data.list[5]
    })
    await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname }}).then(res => {
        cname = res.data.list[0]
    })        
    console.log(pname);
    console.log(cname);
}
getData()

注意上面第二个代码中pname和cname没有用const定义,是因为当我在外面用

const pname = []
const cname = []

定义时发现报出错误:Assignment to constant variable(把常量赋值给了变量)

这是因为我们使用 const 定义了变量且存在初始值。 后面又给这个变量赋值,所以报错了。ES6 标准引入了新的关键字 const 来定义常量,const 与 let 都具有块级作用域:使用 const 定义的常量,不能修改它的值,且定义的常量必须赋初值;let 定义的是变量,可以进行变量赋值操作,且不需要赋初值。这个错误就是因为我们修改了常量而引起的错误,虽然某些浏览器不报错,但是无效果!所以解决方法就是前面加:

let pname = []
let cname = []

成功解决问题。

– 这里debug的时候发现const和let这块还有点含糊,const似乎最好是赋值一个对象或数组,再对对象进行赋值。等我哪天彻底搞明白了后单独出一期讲讲_(:3 ⌒゙)__

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值