← 返回首页
Javascript基础教程(四十九)
发表时间:2021-09-17 23:34:37
地狱回调

1.什么是地狱回调

在使用JavaScript时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱。

Promise和asyn/await的主要应用场景在于解决地狱回调问题。我们来看以下问题: 以下代码分别定义了吃火锅,喝茶,洗澡三个方法。我们期望的执行顺序也是吃完火锅,再喝茶,最后洗澡。

<script>
    //喝茶
    function drinkTea(fn){
        setTimeout(()=>fn('开始喝茶了...'),2000);
    }

    //洗澡
    function bath(fn){
        setTimeout(()=>fn('开始洗澡了....'),3000);
    }

    //吃火锅
    function eatHotPot(fn){
        setTimeout(()=>fn('开始吃火锅了...'),5000);
    }

    eatHotPot(function(resp){console.log(resp)});
    drinkTea(function(resp){console.log(resp)});
    bath(function(resp){console.log(resp)});

</script>

但是由于吃火锅时间最长,控制台输出顺序如下:

开始喝茶了...
开始洗澡了....
开始吃火锅了...

所以我们必须通过回调函数保证执行顺序,程序改写如下:

<script>
    //喝茶
    function drinkTea(fn){
        setTimeout(()=>fn('开始喝茶了...'),2000);
    }

    //洗澡
    function bath(fn){
        setTimeout(()=>fn('开始洗澡了....'),3000);
    }

    //吃火锅
    function eatHotPot(fn){
        setTimeout(()=>fn('开始吃火锅了...'),5000);
    }

    //改写后顺序正确,但是会出现地狱回调现象.
    eatHotPot((resp)=>{
        console.log(resp);
        //吃完火锅喝茶
        drinkTea((resp)=>{
            console.log(resp);
            //喝完茶洗澡
            bath((resp)=>{
                console.log(resp);
            })
        })
    })
</script>

这样执行结果如下:

开始吃火锅了...
开始喝茶了...
开始洗澡了....

但是这个随着要执行事件的增多,会出现恐怖的地狱回调现象。

2.使用Promise改造

<script>
    //喝茶
    function drinkTea() {
        return new Promise(function (resolve) {
            setTimeout(() => resolve('开始喝茶了...'), 2000);
        })
    }

    //洗澡
    function bath() {
        return new Promise(function (resolve) {
            setTimeout(() => resolve('开始洗澡了....'), 3000);
        })

    }

    //吃火锅
    function eatHotPot() {
        return new Promise(function (resolve) {
            setTimeout(() => resolve('开始吃火锅了...'), 5000);
        })
    }

    let promise = eatHotPot();
    promise.then((resp) => {
        console.log(resp);
        return drinkTea();
    }).then((resp)=>{
        console.log(resp);
        return bath();
    }).then((resp)=>{
        console.log(resp);
    })

</script>

输出结果如下:

开始吃火锅了...
开始喝茶了...
开始洗澡了....

3.使用async/await改造

<script>
    //喝茶
    function drinkTea() {
        return new Promise(function (resolve) {
            setTimeout(() => resolve('开始喝茶了...'), 2000);
        })
    }

    //洗澡
    function bath() {
        return new Promise(function (resolve) {
            setTimeout(() => resolve('开始洗澡了....'), 3000);
        })

    }

    //吃火锅
    function eatHotPot() {
        return new Promise(function (resolve) {
            setTimeout(() => resolve('开始吃火锅了...'), 5000);
        })
    }

    async function haveGoodTime(){
        //直接获得resolve返回的异步数据。
        let hotPot = await  eatHotPot();
        console.log(hotPot);
        let tea = await drinkTea();
        console.log(tea);
        let myBath = await bath();
        console.log(myBath);
    }

    haveGoodTime();

</script>

这样执行结果如下:

开始吃火锅了...
开始喝茶了...
开始洗澡了....

使用async/await改造使得异步代码的执行更像是同步代码,可读性也变的更好了。