1. 程式人生 > 實用技巧 >事件和回撥函式的缺陷--回撥地獄、非同步之間的聯絡

事件和回撥函式的缺陷--回撥地獄、非同步之間的聯絡

事件和回撥函式的缺陷

什麼叫非同步處理,就是我們要做一件事,這件事分成兩個部分,這件事做著做著突然發生一件事 ,這件事要等一會才能知道結果,這就非同步場景。比如setTimeout,要過段時間才處理,再比如說,事件,我們註冊事件,和觸發事件就是分開的

事件:某個物件的屬性是一個函式,當發生某一件事時,執行該函式

dom.onclick = function (){
    console.log("元素物件被點選了")
}

這裡我們給dom元素註冊一個點選事件,注意哦這時候還沒有點選這個dom元素,當我們點選這個dom元素後才會觸發事件列印1,這裡就是註冊事件和觸發事件是分開的。

回撥:執行某一個函式來實現一個功能的時候,傳入一個函式作為引數,當發生某件事的時候,會執行該函式

最典型的就是ajax請求了,在向伺服器傳送請求,請求成功伺服器返回資料,對這些資料進行處理的函式

dom.onclick = function (){
    console.log("元素物件被點選了")
}

dom.addEventListener("click",function(){
    console.log("該元素物件被點選後,觸發的回撥函式")
})

本質上,事件和回撥並沒有本質的區別,只是函式放置的位置不同而已

一直以來,使用傳統的回撥或事件處理來解決非同步問題的這種模式都運作良好。直到前端工程越來越複雜...這種處理非同步的方法就出現了下面的兩個問題:

1.回撥地獄:某個非同步操作需要等待之前的非同步操作完成,無論用回撥還是事件,都會陷入不斷的巢狀中

回撥地獄1:

這裡按鈕3必須要等到按鈕2被點選了才會被註冊事件,而按鈕2又必須要等到按鈕1被點選了才會被註冊事件

<body>
    <p>
        <button id="btn1">按鈕1:給按鈕2註冊點選事件</button>
        <button id="btn2">按鈕2:給按鈕3註冊點選事件</button>
        <button id="btn3">按鈕3:點選彈出Hello</button>
    </p>
    <script>
        const btn1 = document.querySelector("#btn1")
        const btn2 = document.querySelector("#btn2")
        const btn3 = document.querySelector("#btn3")

        // btn1.onclick = function(){
        //     btn2.onclick = function(){
        //         btn3.onclick = function (){
        //             alert("Hello")
        //         }
        //     }
        // }
       
      btn1.addEventListener("click",function(){
            btn2.addEventListener('click',function(){
                btn3.addEventListener('click',function(){
                    alert('Hello')
                })
            })
        })
    </script>
</body>

回撥地獄2:吳家長心中有三個女神,有一天,吳家長決定向第一個女神表白,如果女神拒絕了,這向第二個女神表白,直到所有的女神拒絕或者有一個女神同意了

<body>
    <script>
        /**
         * 吳家長心中有三個女神,
         * 有一天,吳家長決定向第一個女神表白,如果女神拒絕了,這向第二個女神表白,直到所有的女神拒絕
         * 或者有一個女神同意了
         */
        function biaobai(god, callback) {
            console.log(`吳家長向【女神${god}】發出了表白簡訊`);
            setTimeout(() => {
                if (Math.random() < 0.3) {
                    callback(true);
                } else {
                    callback(false);
                }
            }, 1000);
        }
        biaobai(1, function (result) {
            if (result) {
                console.log("女神1同意了,吳家長很開心");
            } else {
                console.log(
                    "女神1拒絕了,吳家長表示無壓力,然後接著繼續向下一位女神表白"
                );
                biaobai(2, function (result) {
                    if (result) {
                        console.log("女神2同意了,吳家長很開心");
                    } else {
                        console.log(
                            "女神2拒絕了,吳家長表示無壓力,然後接著繼續向下一位女神表白"
                        );
                        biaobai(3, function (result) {
                            if (result) {
                                console.log("女神3同意了,吳家長很開心");
                            } else {
                                console.log(
                                    "女神3拒絕了,吳家長生無可戀,表示還需要在撒一張更大的網"
                                );
                            }
                        });
                    }
                });
            }
        });
    </script>
</body>

這樣的程式碼巢狀太深

非同步之間的聯絡:某個非同步操作要等待多個非同步操作的結果,對這種聯絡的處理,會讓程式碼的複雜度劇增

這次吳家長撒了一張更大的網了,這次要向二十位女神表白,決定要用更高效的方法;

他同時向二十位女神表白,如果有女神同意,就拒絕其他的女神,並且當所有的女神回覆完成後

他要把所有的回覆都記錄到日誌中進行分析

<body>
    <script>
        function biaobai(god, callback) {
            console.log(`吳家長向【${god}】發出了表白簡訊`);
            setTimeout(() => {
                if (Math.random() < 0.06) {

                    callback(true);
                } else {
                    callback(false);
                }
            }, Math.floor(Math.random() * (3000 - 1000) + 1000))
        }

        let agreeGod = null
        const results = []
        for (let i = 1; i <= 20; i++) {
            biaobai(`女神${i}`, (result) => {
                results.push(result)
                if (result) {
                    console.log(`女神${i}同意了`);
                    //這裡我們要判斷一下只有一個女神同意了,就拒絕其他同意的女神
                    //但是這裡我們要怎麼做呢?因為result不同非同步處理返回的結果
                    //我們要在一個非同步裡面知道其他非同步的結果
                    //可以設定一個全域性變數,(鎖)
                    if(agreeGod){
                        console.log('不好意思,剛才是我朋友用我手機,亂髮的')
                    }else{
                        agreeGod = `女神${i}`
                        console.log('吳家長終於找到真愛了')
                    }
                } else {
                    console.log(`女神${i}拒絕了`)
                }
                                //記錄到日誌,但是寫在這裡不怎麼符合邏輯,記錄日誌這件事應該單獨抽離出來
                if(results.length === 20){
                    console.log('記錄到日誌', results)
                }
            })
        }
    </script>
</body>

上面拒絕其他同意的女神,類似這樣的操作過多的話就需要設定多個的全域性變數,這就會讓程式碼更加的複雜,也會汙染全域性。類似記錄日誌這件事的應該都單獨抽離出來,這樣子程式碼多了 邏輯也不會亂,該完成什麼的就完成什麼,不會過多的操作。