简述回调地狱现象以及解决方法

1,223 阅读2分钟

1. 回调地狱的出现形式

举个栗子

早上,我去早餐店吃早餐,按照习惯叫了豆浆和油条,因为豆浆是早就做好的,而油条是现炸的, 我不可能马上就能吃到,假设豆浆在1s后拿到,油条2s之后拿到,但是吃的时候我都习惯先吃一口油条,再喝一口豆浆,那么就有了以下:

function SoyaBeanMilk(fn){
      setTimeout(()=>{
        fn('豆浆') 
      },1000)
    }
    
    function friedBreadStick(fn){
      setTimeout(()=>{
        fn('油条') 
      },2000)
    }

    friedBreadStick(function(data){
      console.log(data);
      SoyaBeanMilk(function(data){
        console.log(data);
      })
    })

输出结果:先2s拿到油条,再1s拿到豆浆

油条豆浆.gif

2. 回调地狱现象

当吃完油条,喝完豆浆,我依旧没有吃饱,在吃点别的呢,渐渐地就产生了回调地狱现象

在跟老板要两个包子,再来几个烧麦,就产生了如下:

friedBreadStick(function(data){
      console.log(data);
      SoyaBeanMilk(function(data){
        console.log(data);
          XXX(function(data){
          console.log(data);
            XXX(function(data){
            console.log(data);
              XXX(function(data){
              console.log(data);
            })
          })
        })
      })
    })

这一坨代码看得人细思极恐,像便便一样,不方便维护

于是,es6引出了promise对象

3. promise对象

promise的详细用法请看===> js中的promise对象

promise中通过resolve参数将异步的数据传出去,再通过then获取

接下来对上面代码进行改造

    function SoyaBeanMilk(){
      return new Promise(function(resolve){
        setTimeout(()=>{
          resolve('豆浆') 
        },1000)
      })
      
    }
    
    function friedBreadStick(){
      return new Promise(function(resolve){
        setTimeout(()=>{
          resolve('油条') 
        },2000)
      })
      
    }

    friedBreadStick().then(function(data){
      console.log(data);
      return SoyaBeanMilk()
    }).then(function(data){
      console.log(data);
    })

结果依旧还是如此油条豆浆.gif

使用了promise的链式写法,这样代码稍微简洁了些,但是还是有些不好维护

于是,再次使用async,await

4. async await

await后面加一个promise对象就直接可以拿到resolve返回的值,并且可以赋值

await会等待后面的异步程序执行完毕之后再去执行下面的代码,所以看起来跟同步代码一样

    async function getBreakfast(){
      let friedBreadStick = await friedBreadStick();
      console.log(friedBreadStick);
      let SoyaBeanMilk = await SoyaBeanMilk();
      console.log(SoyaBeanMilk);
    }

优点:(更像是同步代码)结构清晰,方便维护,解决回调地狱