再了解什么是回调地狱前要先简单理解回调函数能够实现异步编程(可以控制函数的调用顺序)
回调函数
当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,
这种函数就称为回调函数。我们熟悉的定时器和Ajax中就存在有回调函数.
例如
setTimeout(function(){
console.log("apple");
setTimeout( function(){
console.log("banana");
setTimeout(function(){
console.log("cherry");
},1000)
},2000)
},3000
)
等待 3 秒后,打印 "apple"。
等待 2 秒后(从第一个 setTimeout 回调开始算起),打印 "banana"。
等待 1 秒后(从第二个 setTimeout 回调开始算起),打印 "cherry"。
回调地狱
回调地狱(Callback Hell),也被称为“Pyramid of Doom”,是指在JavaScript中使用回调函数嵌套过多、层级过深,导致代码难以理解、难以维护和可读性差的一种情况。
这种情况通常出现在异步操作的场景,比如处理文件读取、数据库查询、网络请求等。当多个异步操作依赖于前一个操作的结果时,使用回调函数嵌套的方式可能会导致代码如下所示:
asyncFunction1(function (result1) {
asyncFunction2(result1, function (result2) {
asyncFunction3(result2, function (result3) {
// ... 更多嵌套的回调函数 ...
});
});
});
为了解决回调地狱问题,可以采用以下几种方式:
1.使用Promise: 使用Promise可以改善回调地狱,将回调函数的嵌套变成链式调用。Promise提供了更清晰、更可读的异步代码编写方式。有三种状态:1.pending 2.fulfilled 3.rejected
Promise是js中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。
Promise(构造函数接收一个函数作为参数),我们需要处理的异步任务就卸载该函数体内,该函数的两个参数是resolve,reject。
异步任务执行成功时调用resolve函数返回结果,反之调用reject。
Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据。
Promise的链式编程可以保证代码的执行顺序,前提是每一次在than做完处理后,
一定要return一个Promise对象,这样才能在下一次then时接收到数据。
function fn(str){
var p=new Promise(function(resolve,reject){
//处理异步任务
var flag=true
if (flag){
resolve(str)
}
else{
reject('操作失败')
}
})
return p
}
fn('我们作为学生')
.then((data)=>{
console.log(data);
return fn('就要好好学习');
})
.then((data)=>{
console.log(data);
return fn('天天向上')
})
.then((data)=>{
console.log(data);
})
.catch((data)=>{
console.log(data)
})
但是Promise最大的问题就是代码冗余,原来的异步任务被Promise封装一下,不管什么操作都用than,
就会导致一眼看过去全是then…then…then…,这样也是不利于代码维护的
2.使用Async/Await: Async/Await 是 Promise 的语法糖,它进一步简化了异步代码的编写,使得代码更加同步和易读。
async 关键字放在函数声明或函数表达式前面,表示该函数会返回一个 Promise。 是函数成为异步执行
async function fn(){
var flag=true
if (flag){
return '12345'
}
else{
throw '处理失败'
}
}
fn()
.then(data=>{
console.log(data)
})
.catch(data=>{
console.log(data)
})
console.log('先执行我,表明async声明的函数是异步的');
运行结果:
结果先执行我,表明async声明的函数是异步的
12345