浅谈JS中的回调地狱

要想了解JS中的回调地狱,首先要从两个概念说起:

一.回调函数:

       当一个函数作为参数传入另一个函数中,并且它不会立即执行,只有在满足一定条件时,才可以执行,这种函数就称为回调函数。

 setTimeout(function(){
        console.log("执行了回调函数");
      },3000)

在上述代码中,function就是一个回调函数,它作为一个参数传递到定时器函数中,只有在满足延时3秒的条件后,才会执行该回调函数。

二.异步任务:

      我们都了解js是异步的。任务类型分为同步任务和异步任务:同步任务在主线程上排队执行,只有当前一个任务执行完毕之后,才能执行下一个任务;而同步任务不进入主线程,而是进入异步任务队列,前一个任务是否执行完毕不影响下一个任务的执行。

  setTimeout(function(){
        console.log("执行了回调函数");
      },3000)
      console.log("输出111,执行异步任务");

         

从上述的代码示例可以看出,定时器中的任务是否执行完毕不影响后面 任务的执行,当定时器需要等待3秒再去执行时,不会阻塞后面函数的执行。这就是简单的异步任务的实现。

三.JS中回调地狱是什么:

    根据上述对回调函数和异步任务的介绍我们可以得出一个结论。存在异步任务的代码,不能保证代码能够按照顺序执行,但是如果我们必须要让代码按照顺序执行呢?
假如要说一句话,语序是这样的:早上起床先睁开眼睛,再去洗漱,最后穿衣服。

如果我们要让上面的话按照语序输出,就需要这样操作:

   setTimeout(function(){
        console.log("早上起床先睁开眼睛");
        setTimeout(function(){
            console.log("再去洗漱");
            setTimeout(function(){
                console.log("最后穿衣服");
            },1000)
        },2000)
    },3000)

从上面的实例可以看出,如果要按照语序输出一句话,就需要回调函数嵌套回调函数,上面代码就嵌套了三层回调函数,这种回调函数中嵌套回调函数的情况就称为回调地狱。

回调地狱是为了实现代码执行操作顺序而出现的一种操作,回调地狱会造成带代码的可读性非常差并且不利于后期代码的维护。

四.解决回调地狱的方法:

   4.1使用Promise

       promise是js原生的一个对象,是解决异步编程的一种方案。

       1.promise构造函数接收一个函数作为参数,在该函数体内就可以写我们要执行的异步任务,p该函数的两个参数是resolve和reject。当异步任务成功执行时调用resolve函数返回结果,如果失败就调用reject函数。

       2.promise对象中的then方法用来接收处理成功时候的相应数据,catch方法用来接收处理失败时的相应数据。

       3.promise的链式编程可以保证代码的执行顺序,但是前提是每一次在执行完then中的操作时,必须要返回一个promise对象,这样才能保证下一次的then时能够接收到数据.

  function fn(str){
        var p=new Promise(function(resolve,reject){
            var flag=true;
            setTimeout(function(){
                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的问题就是代码冗余,使用promise封装异步任务就会出现很多then,这样也是不利于代码维护。

4.2使用async/await

     async作为关键字放到一个声明函数的前面,声明该函数是一个异步任务,不会阻塞后面任务的执行

async function fn(){
        console.log('张峻豪最帅');
    }
    console.log(fn());

我们可以看出使用async关键字将函数自动封装成了一个promise对象,所以就可以在使用async中的函数中使用promise的方法。

await关键字只能在使用async声明的函数中使用,await后面可以直接跟一个promise实例对象,可以直接拿到promise中resolve返回的数据。

    //封装一个返回promise的异步任务
    function fn(str){
        var p=new Promise(function(resolve,reject){
            var flag=true;
            setTimeout(function(){
                if(flag){
                    resolve(str)
                }
                else{
                    reject('处理失败')
                }
            },1000)
        })
        return p;
    }
    async function test(){
        var res1=await fn('早上起床先睁开眼睛');
        var res2=await fn('再去洗漱');
        var res3=await fn('最后穿上衣服');
        console.log(res1,res2,res3);
    }
    test();

上述代码执行结果如下:

 async和await实际上是ES7提供给我们的语法糖,这样就解决了使用原生promise解决回调地狱中遇到的代码可读性不高,代码冗余和代码不利于维护的问题,使一个异步代码看起来更像是一个同步代码。

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值