成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

淺談 Underscore.js 中 _.throttle 和 _.debounce 的差異

miracledan / 2477人閱讀

摘要:如果想忽略結(jié)尾邊界上的調(diào)用,傳入返回客戶(hù)調(diào)用函數(shù)上次執(zhí)行時(shí)間點(diǎn)延遲執(zhí)行函數(shù)若設(shè)定了開(kāi)始邊界不執(zhí)行選項(xiàng),上次執(zhí)行時(shí)間始終為首次執(zhí)行時(shí),如果設(shè)定了開(kāi)始邊界不執(zhí)行選項(xiàng),將上次執(zhí)行時(shí)間設(shè)定為當(dāng)前時(shí)間。

  

Underscore.js 是一個(gè)很精干的庫(kù),壓縮后只有5.2KB。它提供了幾十種函數(shù)式編程的方法,彌補(bǔ)了標(biāo)準(zhǔn)庫(kù)的不足,大大方便了JavaScript的編程。

本文僅探討Underscore.js的兩個(gè)函數(shù)方法 _.throttle_.debounce 的原理、效果和用途。

通常的函數(shù)(或方法)調(diào)用過(guò)程分為三個(gè)部分:請(qǐng)求、執(zhí)行和響應(yīng)。(文中“請(qǐng)求”與“調(diào)用”同義,“響應(yīng)”與“返回”同義,為了更好的表述,刻意采用請(qǐng)求和響應(yīng)的說(shuō)法。)

某些場(chǎng)景下,比如響應(yīng)鼠標(biāo)移動(dòng)或者窗口大小調(diào)整的事件,觸發(fā)頻率比較高。若稍處理函數(shù)微復(fù)雜,需要較多的運(yùn)算執(zhí)行時(shí)間,響應(yīng)速度跟不上觸發(fā)頻率,往往會(huì)出現(xiàn)延遲,導(dǎo)致假死或者卡頓感。

在運(yùn)算資源不夠的時(shí)候,最直觀的解決辦法就是升級(jí)硬件,誠(chéng)然通過(guò)購(gòu)買(mǎi)更好的硬件可以解決部分問(wèn)題,但是也需要為此付出高額的成本。特別是客戶(hù)端和服務(wù)器模式,要求客戶(hù)端統(tǒng)一升級(jí)硬件基本不可能。

在資源有限的前提下,處理函數(shù)無(wú)法即時(shí)響應(yīng)高頻調(diào)用。退而求其次,只響應(yīng)部分請(qǐng)求是否可行呢?某些場(chǎng)景下的密集性請(qǐng)求,具備很強(qiáng)的同質(zhì)和連續(xù)性。比如說(shuō),鼠標(biāo)移動(dòng)的軌跡參數(shù)。響應(yīng)越及時(shí)效果越平滑,但是如果響應(yīng)速度跟不上時(shí),反而會(huì)出現(xiàn)卡頓感,如果適當(dāng)?shù)膩G棄一些請(qǐng)求效果更流暢。

throttledebounce 是解決請(qǐng)求和響應(yīng)速度不匹配問(wèn)題的兩個(gè)方案。二者的差異在于選擇不同的策略。

電梯超時(shí)

想象每天上班大廈底下的電梯。把電梯完成一次運(yùn)送,類(lèi)比為一次函數(shù)的執(zhí)行和響應(yīng)。假設(shè)電梯有兩種運(yùn)行策略 throttledebounce ,超時(shí)設(shè)定為15秒,不考慮容量限制。

throttle 策略的電梯。保證如果電梯第一個(gè)人進(jìn)來(lái)后,15秒后準(zhǔn)時(shí)運(yùn)送一次,不等待。如果沒(méi)有人,則待機(jī)。

debounce 策略的電梯。如果電梯里有人進(jìn)來(lái),等待15秒。如果又人進(jìn)來(lái),15秒等待重新計(jì)時(shí),直到15秒超時(shí),開(kāi)始運(yùn)送。

使用示例 _.throttle 使用示例
function log( event ) {
  console.log( $(window).scrollTop(), event.timeStamp );
};

// 控制臺(tái)記錄窗口滾動(dòng)事件,觸發(fā)頻率比你想象的要快
$(window).scroll( log );

// 控制臺(tái)記錄窗口滾動(dòng)事件,每250ms最多觸發(fā)一次
$(window).scroll( _.throttle( log, 250 ) );
_.debounce 使用示例
function ajax_lookup( event ) {
  // 對(duì)輸入的內(nèi)容$(this).val()執(zhí)行 Ajax 查詢(xún)
};

// 字符輸入的頻率比你預(yù)想的要快,Ajax 請(qǐng)求來(lái)不及回復(fù)。
$("input:text").keyup( ajax_lookup );

// 當(dāng)用戶(hù)停頓250毫秒以后才開(kāi)始查找
$("input:text").keyup( _.debounce( ajax_lookup. 250 ) );
underscore源碼注解

讓我們來(lái)讀讀源碼,探其究竟?;陂_(kāi)發(fā)版本(1.7.0)的源碼,加上了一些注釋以幫助理解。

_.throttle方法源碼
/**
 * 頻率控制 返回函數(shù)連續(xù)調(diào)用時(shí),func 執(zhí)行頻率限定為 次 / wait
 * 
 * @param  {function}   func      傳入函數(shù)
 * @param  {number}     wait      表示時(shí)間窗口的間隔
 * @param  {object}     options   如果想忽略開(kāi)始邊界上的調(diào)用,傳入{leading: false}。
 *                                如果想忽略結(jié)尾邊界上的調(diào)用,傳入{trailing: false}
 * @return {function}             返回客戶(hù)調(diào)用函數(shù)   
 */
_.throttle = function(func, wait, options) {
  var context, args, result;
  var timeout = null;
  // 上次執(zhí)行時(shí)間點(diǎn)
  var previous = 0;
  if (!options) options = {};
  // 延遲執(zhí)行函數(shù)
  var later = function() {
    // 若設(shè)定了開(kāi)始邊界不執(zhí)行選項(xiàng),上次執(zhí)行時(shí)間始終為0
    previous = options.leading === false ? 0 : _.now();
    timeout = null;
    result = func.apply(context, args);
    if (!timeout) context = args = null;
  };
  return function() {
    var now = _.now();
    // 首次執(zhí)行時(shí),如果設(shè)定了開(kāi)始邊界不執(zhí)行選項(xiàng),將上次執(zhí)行時(shí)間設(shè)定為當(dāng)前時(shí)間。
    if (!previous && options.leading === false) previous = now;
    // 延遲執(zhí)行時(shí)間間隔
    var remaining = wait - (now - previous);
    context = this;
    args = arguments;
    // 延遲時(shí)間間隔remaining小于等于0,表示上次執(zhí)行至此所間隔時(shí)間已經(jīng)超過(guò)一個(gè)時(shí)間窗口
    // remaining大于時(shí)間窗口wait,表示客戶(hù)端系統(tǒng)時(shí)間被調(diào)整過(guò)
    if (remaining <= 0 || remaining > wait) {
      clearTimeout(timeout);
      timeout = null;
      previous = now;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    //如果延遲執(zhí)行不存在,且沒(méi)有設(shè)定結(jié)尾邊界不執(zhí)行選項(xiàng)
    } else if (!timeout && options.trailing !== false) {
      timeout = setTimeout(later, remaining);
    }
    return result;
  };
};
_.debounce方法源碼
/**
 * 空閑控制 返回函數(shù)連續(xù)調(diào)用時(shí),空閑時(shí)間必須大于或等于 wait,func 才會(huì)執(zhí)行
 *
 * @param  {function} func        傳入函數(shù)
 * @param  {number}   wait        表示時(shí)間窗口的間隔
 * @param  {boolean}  immediate   設(shè)置為ture時(shí),調(diào)用觸發(fā)于開(kāi)始邊界而不是結(jié)束邊界
 * @return {function}             返回客戶(hù)調(diào)用函數(shù)
 */
_.debounce = function(func, wait, immediate) {
  var timeout, args, context, timestamp, result;

  var later = function() {
    // 據(jù)上一次觸發(fā)時(shí)間間隔
    var last = _.now() - timestamp;

    // 上次被包裝函數(shù)被調(diào)用時(shí)間間隔last小于設(shè)定時(shí)間間隔wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last);
    } else {
      timeout = null;
      // 如果設(shè)定為immediate===true,因?yàn)殚_(kāi)始邊界已經(jīng)調(diào)用過(guò)了此處無(wú)需調(diào)用
      if (!immediate) {
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      }
    }
  };

  return function() {
    context = this;
    args = arguments;
    timestamp = _.now();
    var callNow = immediate && !timeout;
    // 如果延時(shí)不存在,重新設(shè)定延時(shí)
    if (!timeout) timeout = setTimeout(later, wait);
    if (callNow) {
      result = func.apply(context, args);
      context = args = null;
    }

    return result;
  };
};
可視化演示

示例中每一行都以30ms的速度繪制時(shí)間軸,第一行Mousemove Events是參考基準(zhǔn),以50ms每次的響應(yīng)頻率,在時(shí)間軸上輸出循環(huán)可見(jiàn)ASCII碼字符。

當(dāng)鼠標(biāo)進(jìn)入左側(cè)方型區(qū)域(mouseenter 事件)所有行開(kāi)始繪制時(shí)間軸, 鼠標(biāo)晃動(dòng)(mousemove 事件)會(huì)在時(shí)間軸上繪制字符塊,每個(gè)字符塊表示事件被觸發(fā)一次。為了展現(xiàn)延遲觸發(fā)效果,相鄰字符塊的演示和文字是不同的。

頂部的兩個(gè)按鈕每100毫秒觸發(fā)1次每200毫秒觸發(fā)2次演示以固定頻率勻速觸發(fā)事件的效果。

演示地址:http://throttle-debounce.coding.io/

源碼地址:https://coding.net/u/duwan/p/throttle-debounce/

使用場(chǎng)景

只要牽涉到連續(xù)事件或頻率控制相關(guān)的應(yīng)用都可以考慮到這兩個(gè)函數(shù),比如:

游戲射擊,keydown 事件

文本輸入、自動(dòng)完成,keyup 事件

鼠標(biāo)移動(dòng),mousemove 事件

DOM 元素動(dòng)態(tài)定位,window 對(duì)象的 resize 和 scroll 事件

前兩者 debounce 和 throttle 都可以按需使用;后兩者肯定是用 throttle 了。如果不做過(guò)濾處理,每秒種甚至?xí)|發(fā)數(shù)十次相應(yīng)的事件。尤其是 mousemove 事件,每移動(dòng)一像素都可能觸發(fā)一次事件。如果是在一個(gè)畫(huà)布上做一個(gè)鼠標(biāo)相關(guān)的應(yīng)用,過(guò)濾事件處理是必須的,否則肯定會(huì)造成糟糕的體驗(yàn)。

參考閱讀

UNDERSCORE.JS

高階函數(shù) debounce 和 throttle

jQuery throttle / debounce: Sometimes, less is more!

Debounce and Throttle: a visual explanation

  

Vangie Du

將來(lái)的你,一定會(huì)感謝現(xiàn)在拼命努力的自己!

技術(shù)博客: http://blog.coding.net/

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85359.html

相關(guān)文章

  • 淺談 Underscore.js _.throttle _.debounce 差異

    摘要:如果想忽略結(jié)尾邊界上的調(diào)用,傳入返回客戶(hù)調(diào)用函數(shù)上次執(zhí)行時(shí)間點(diǎn)延遲執(zhí)行函數(shù)若設(shè)定了開(kāi)始邊界不執(zhí)行選項(xiàng),上次執(zhí)行時(shí)間始終為首次執(zhí)行時(shí),如果設(shè)定了開(kāi)始邊界不執(zhí)行選項(xiàng),將上次執(zhí)行時(shí)間設(shè)定為當(dāng)前時(shí)間。 文章轉(zhuǎn)自:https://blog.coding.net/blog/...注: _.throttle 和 _.debounce是Underscore.js庫(kù)的兩個(gè)針對(duì)函數(shù)節(jié)流的方法,用于處理高頻...

    alighters 評(píng)論0 收藏0
  • 【譯】通過(guò)例子解釋 Debounce Throttle

    摘要:舉例舉例通過(guò)拖拽瀏覽器窗口,可以觸發(fā)很多次事件。不支持,所以不能在服務(wù)端用于文件系統(tǒng)事件??偨Y(jié)將一系列迅速觸發(fā)的事件例如敲擊鍵盤(pán)合并成一個(gè)單獨(dú)的事件。確保一個(gè)持續(xù)的操作流以每毫秒執(zhí)行一次的速度執(zhí)行。 Debounce 和 Throttle 是兩個(gè)很相似但是又不同的技術(shù),都可以控制一個(gè)函數(shù)在一段時(shí)間內(nèi)執(zhí)行的次數(shù)。 當(dāng)我們?cè)诓僮?DOM 事件的時(shí)候,為函數(shù)添加 debounce 或者 th...

    LeoHsiun 評(píng)論0 收藏0
  • throttledebounce區(qū)別

    摘要:自己嘗試一下年在的文章中第一次看到的實(shí)現(xiàn)方法。這三種實(shí)現(xiàn)方法內(nèi)部不同,但是接口幾乎一致。如你所見(jiàn),我們使用了參數(shù),因?yàn)槲覀冎粚?duì)用戶(hù)停止改變?yōu)g覽器大小時(shí)最后一次事件感興趣。 前幾天看到一篇文章,我的公眾號(hào)里也分享了《一次發(fā)現(xiàn)underscore源碼bug的經(jīng)歷以及對(duì)學(xué)術(shù)界拿來(lái)主義的思考》具體文章詳見(jiàn),微信公眾號(hào):showImg(https://segmentfault.com/img/b...

    Pluser 評(píng)論0 收藏0
  • web性能優(yōu)化--高性能javascript

    摘要:用局部變量存儲(chǔ)本地范圍之外的變量值,如果它們?cè)诤瘮?shù)中的使用多于一次。將它的值存入一個(gè)局部變量,消除一次搜索過(guò)程。地將此值存入一個(gè)局部變量中。 總結(jié)了一下《高性能javascript》書(shū)中比較核心的點(diǎn),并補(bǔ)充了一些點(diǎn)。 第一章 DOM標(biāo)簽 將所有 標(biāo)簽放置在頁(yè)面的底部,緊靠 body 關(guān)閉標(biāo)簽的上方。此法可以保證頁(yè)面在腳本 運(yùn)行之前完成解析。 將腳本成組打包。頁(yè)面的 標(biāo)簽越少,頁(yè)面的加...

    ytwman 評(píng)論0 收藏0
  • [譯]通過(guò)實(shí)例講解DebouncingThrotting(防抖與節(jié)流)

    摘要:譯通過(guò)實(shí)例講解和防抖與節(jié)流源碼中推薦的文章,為了學(xué)習(xí)英語(yǔ),翻譯了一下原文鏈接作者本文來(lái)自一位倫敦前端工程師的技術(shù)投稿。首次或立即你可能發(fā)現(xiàn)防抖事件在等待觸發(fā)事件執(zhí)行,直到事件都結(jié)束后它才執(zhí)行。 [譯]通過(guò)實(shí)例講解Debouncing和Throtting(防抖與節(jié)流) lodash源碼中推薦的文章,為了學(xué)習(xí)(英語(yǔ)),翻譯了一下~ 原文鏈接 作者:DAVID CORBACHO 本文來(lái)自一位...

    Jenny_Tong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<