首頁 > 軟體

JS按鈕連擊和介面呼叫頻率限制防止客戶爆倉

2022-10-02 14:01:28

背景

這個專案是一個貨幣交易使用者端,後端會走幣安的開放介面,而幣安的介面每分鐘呼叫次數是有閾值的,調多了直接介面返回錯誤。

使用者端裡,有的視窗可能涉及 多個資訊的查詢 ,而這些資訊需要呼叫不同的幣安的介面,因此後端有的介面呼叫起來 權重很大(存在一個介面需要呼叫幣安十幾個介面的情況)。

那麼介面呼叫權重大的有兩個視窗,其中一個是賬戶資訊視窗。

賬戶資訊視窗需要實時的更新持倉盈虧以及強平價、開倉價等資訊,這些資訊分佈在幣安各個介面裡,所以呼叫這個介面的 權重很大 。在這個視窗中我們新增了一個 強制重新整理資料 按鈕,用來 防止行情波動大 時卡住,影響 資料實時性

那麼當時的我還是欠考慮,忘記 給按鈕新增防抖操作了,帶來的結果就是在網路狀況不好的情況下,有些比較急躁的使用者會 連擊 ,這樣會一直呼叫介面,權重很快就達到閾值了。達到閾值後平倉平不了,虧錢甚至是爆倉,只能乾瞪眼。

所以我們要 控制使用者連擊行為 ,這就要用到節流了。

另一個呼叫權重大的視窗是交易視窗,委託下單成功後會推播持倉資料、開倉價等。委託單有幾個狀態:掛單、部成(部分成交,多次)或者已成(完全成交,一次),部成狀態和已成狀態都會推播資料,有推播就要調介面。那麼部成的情況下就很容易短時間內(0.5s)達到完全成交,也就是說有可能 一個委託單會觸發好幾次的介面呼叫 。這種使用者端主要功能就是 下單 了,行情波動大的時候交易員都是快捷鍵操作,一秒幾單,這是達到閾值的主要原因,不節流等著提桶吧。

節流是什麼

介紹了這麼多,有的小夥伴還不知道什麼是“節流”,或者是聽過 防抖節流 ,但是一直對這兩個概念混淆,接下來我額外給大家做個小科普。

想必很多人都有玩過 moba 遊戲,我拿大眾點的 英雄聯盟王者榮耀 來舉例。

節流:英雄是會釋放技能的,技能釋放完會有冷卻 cd,如果沒有冷卻完畢,不管你手按的再快,技能都放不出來。這個就是節流,一定時間瘋狂連擊我只觸發一次。

防抖:回城都知道吧,王者榮耀裡回城所需的時間是 7 秒,如果在回城過程中你再次點選回城,那麼回城時間是會被重置的。比如你點選回城過了 3 秒了,這個時間手欠又點了一下回城,好了,原本只要再等 3 秒就能泡泉水,這下你又要重新登 7 秒了。這個就是防抖。

迴歸正題,因為我希望的是 允許使用者重新整理,但是不能太頻繁,最好是一段時間內只允許重新整理一次 ,是不是和上面防抖的例子一樣,妥妥的防抖就安排上了嘛。

如何節流

不使用節流

我們先使用一個簡單的例子來講。

邏輯就是滑鼠在灰色 box 上移動時,不斷遞增數位。

<style>
    .box {
        background-color: grey;
        height: 100px;
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 20px;
        color: #fff;
    }
</style>
<body>
    <div class="box" id="box">0</div>
    <script>
        const box = document.querySelector('#box');
        let count = 0;
        box.addEventListener('mousemove', ()=>{
            box.innerHTML = ++count;
        })
    </script>
</body>

可以看到,正常情況下 mousemove 事件會頻繁觸發。如果換成介面呼叫會咋樣?想都不敢想。

使用節流之後

我們的需求還是滑鼠移動時,數位遞增,不同的是我們希望數位增長不要太快(事件觸發頻繁不要太快),這就要用到防抖了。

我們來改造一下程式碼:

    const box = document.querySelector('#box');
    let count = 0;
    const throttle = (callback) => {
        let time = 0;
        return () => {
            const now = Date.now();
            const diff = (now - time) / 1000;
            if(diff > 0.5) {
                callback();
                time = now;
            }
        }
    }
    box.addEventListener('mousemove', throttle(()=>{
        box.innerHTML = ++count;
    }))

其中,throttle 函數的返回值是一個函數,這個函數參照了外層變數 time,形成了一個閉包。

變數 time 用來記錄 上一次呼叫發生的時間 ,一開始預設為 0 ,這樣下次觸發就能 直接進行第一次呼叫

後續觸發事件回撥時,判斷當前觸發回撥的時間和上一次觸發回撥的 時間差 是不是 大於 我們規定的時間(0.5s),如果大於則允許呼叫,否則本著節流的邏輯,這次呼叫顯然不被允許了。

需要注意的是,在允許呼叫的情況下,我們要 更新 time 的值為 now

我們來看看改造後的效果:

模板

相信大家都看出來了,樸素的節流有一套模板:

const thrrotle = (callback) => {
    let time = 0;
    return () => {
        const now = Date.now();
        const diff = (now - time) / 1000;
        if(diff > 0.5) {
            callback();
            time = now;
        }
    }
}

還有種節流是通過一個 flag 變數控制是否允許呼叫回撥的:

 function throttle(fn,delay) {
    let flag = true;
    return function() {
        if (flag) {
            setTimeout(() => {
                fn.call(this); // 繫結 this
                flag = true;
            }, delay);
        }
        flag = false;
    }
}

範例

那麼我專案中就是控制 10 秒內只允許觸發一次介面呼叫,因此這裡的 0.5 我要改成 10。

// 點選重新整理按鈕嘗試重新整理
const attempRefresh = (() => {
  let lastTime = new Date().getTime();
  const delay = 10;
  return () => {
    const now = new Date().getTime();
    const diff = (now - lastTime) / 1000;
    if (diff >= delay) {
      getAccountInfo(); // 呼叫介面
      lastTime = now;
    } else {
      message.info({
        content: `重新整理過於頻繁,請${delay - Math.floor(diff)}秒後嘗試!`,
        key: EMessageKey.ACCOUNT_INFO,
      });
    }
  };
})();

經過這麼一改造,使用者第一次點選重新整理的時候是允許重新整理的,而在 10 秒內妄圖再次重新整理,展現給它的只有冰冷的提示語。

結束語

日常開發中,除了限制介面呼叫頻率外,像頁面 scroll 事件、視窗 resize 事件,為了效能考慮,都是需要進行節流處理的,而看完本文,相信大家都理解掌握了節流的方法,套用模板就完事了。但是還是希望大家能吃透,畢竟程式碼也不多,有了思路就不用去背程式碼了。學到就是賺到。

以上就是JS按鈕連擊和介面呼叫頻率限制防止客戶爆倉的詳細內容,更多關於JS限制按鈕連擊介面呼叫的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com