目录
一、回调地狱
在了解回调地狱之前,先了解一下 ‘ 回调函数’和‘ 异步任务 ’
1、回调函数
回调函数:
当一个函数作为参数传入另一个函数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。
function fn(){
console.log('陕西西安')
}
setTimeout(fn,3000) //fn就是回调函数
2、异步任务
与之相对应的概念是“同步任务”,同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。
console.log('A');
setTimeout(function () {
console.log('B')
}, 0)
setTimeout(function () {
console.log('C')
}, 0)
setTimeout(function () {
console.log('D')
}, 0)
console.log('E');
输出结果
3、回调地狱
回调地狱:在回调函数中再嵌套回调函数的情况称为回调地狱(是实现代码顺序执行的一种操作方式)
(1)代码的可读性差、可维护性差
(2)代码的扩展性差
setTimeout(function(){
console.log('陕西西安');
setTimeout(function () {
console.log('广东深圳');
setTimeout(function () {
console.log('浙江杭州');
},1000)
},2000)
},3000)
这种回调函数中嵌套回调函数的情况就叫做回调地狱。回调地狱就是为是实现代码顺序执行而出现的一种操作,
二、解决回调地狱问题
回调地狱的问题:
.嵌套层次很深,可读性差,难以维护
.无法正常使用return 和throw
.无法正常检索堆栈信息
.多个回调之间难以建立联系
回调地狱的解决办法:
(1)、Promise
(2)、async/await
1、Promise对象
(1)Promise是js中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。
(2)它通过引入一个回调,避免更多的回调.
简单说Promise就是一个容器,里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise有3个状态
- pending[待定] 初始状态 resloved[实现] 操作成功 rejected[被否决] 操作失败
(3)用法:
A、构造函数有两个参数:resloved和rejected,都是回调函数
B、resloved函数:异步操作成功后调用,并将异步操作的结果传递出去
C、rejected函数:异步操作失败后调用,将失败的信息传递出去
E、.then():接收resloved函数传递过来的信息
F、.catch():接收rejected函数传递过来的信息
Promise 状态发生改变,就会触发.then()里的响应函数处理后续步骤.
Promise 状态一经改变,不会再变.
Promise 实例一经创建,执行器立即执行.
function fn(str) { // str= '陕西西安雁塔区'
//创建promise对象
let p = new Promise(function (resolve, reject) {
let flag = true
setTimeout(function () {
if (flag) { // 模拟异步调用成功
resolve(str) // 将str通过resolve 传递出去 resolve('陕西西安雁塔区')
}
else {
reject('操作失败') // 将失败的信息通过reject传递出去
}
})
})
return p;
}
let tmp = fn('陕西西安雁塔区')
tmp.then((data) => { //data= '陕西西安雁塔区'
console.log(data);
return fn('广东 深圳')
}).then((data) => {//data= '广东 深圳'
console.log(data);
return fn('吉林 延吉')
}).then((data) => {//data= '吉林 延吉'
console.log(data);
}).catch((err) => {
console.log(err);
})
2、async和await函数
(1)为什么有async/await
Promise虽然跳出了异步嵌套的怪圈,用链式表达更加清晰,但是我们也发现如果有大量的异步请求的时候,流程复杂的情况下,会发现充满了屏幕的then,看起来非常吃力,而ES7的async/await的出现就是为了解决这种复杂的情况
例如:实现一个暂停功能,输入N毫秒,则停顿N毫秒后才继续往下执行。
var sleep = function (time){
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('aaaaaa')
}, time);
})
}
var start =async function () {
console.log('------start---------');
await sleep(3000).then(data => {
console.log(data);
})
console.log('------end---------');
}
start();
async 在这里使用起来就像同步代码那样直观
(2)使用async/await基本规则
- uawait关键字只能在使用async定义的函数中使用
- uawait后面可以直接跟一个 Promise实例对象(可以跟任何表达式,更多的是跟一个返回Promise对象的表达式)
- uawait函数不能单独使用
- uawait可以直接拿到Promise中resolve中的数据。
function fn(str) { // str= '陕西西安雁塔区'
//创建promise对象
let p = new Promise(function (resolve, reject) {
let flag = true
setTimeout(function () {
if (flag) { // 模拟异步调用成功
resolve(str) // 将str通过resolve 传递出去 resolve('陕西西安雁塔区')
}
else {
reject('操作失败') // 将失败的信息通过reject传递出去
}
})
})
return p;
}
async function test() {
try {
let s1 = await fn('重庆');
let s2 = await fn('深圳');
let s3 = await fn('杭州');
console.log(s1, s2, s3);
}catch{
console.log(error);
}
}
test();
(3)promise和async/await区别
1、promise是ES6,async/await是ES7
2 、async/await相对于promise来讲,写法更加优雅
3 、reject状态:
1)promise错误可以通过catch来捕捉,建议尾部捕获错误
2)async/await既可以用.then又可以用try-catch捕捉