Простой газ в JS

Я ищу простой газ в JS. Я знаю, что такие библиотеки, как lodash и underscore, есть, но только для одной функции было бы излишне включать любую из этих библиотек.

Я также проверял, имеет ли jquery подобную функцию - не смог найти.

Я нашел один рабочий дроссель, и вот код:

function throttle(fn, threshhold, scope) {
  threshhold || (threshhold = 250);
  var last,
      deferTimer;
  return function () {
    var context = scope || this;

    var now = +new Date,
        args = arguments;
    if (last && now < last + threshhold) {
      // hold on to it
      clearTimeout(deferTimer);
      deferTimer = setTimeout(function () {
        last = now;
        fn.apply(context, args);
      }, threshhold);
    } else {
      last = now;
      fn.apply(context, args);
    }
  };
}

Проблема в том, что она запускает функцию еще раз по истечении времени газа. Итак, давайте предположим, что я сделал дроссель, который срабатывает каждые 10 секунд при нажатии клавиш - если я нажму 2 раза, он все равно будет срабатывать при втором нажатии клавиш, когда 10 секунд будут завершены. Я не хочу такого поведения.

26 ответов

Решение

Я бы использовал исходный код underscore.js или lodash, чтобы найти хорошо протестированную версию этой функции.

Вот немного измененная версия кода подчеркивания, чтобы удалить все ссылки на сам underscore.js:

// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
function throttle(func, wait, options) {
  var context, args, result;
  var timeout = null;
  var previous = 0;
  if (!options) options = {};
  var later = function() {
    previous = options.leading === false ? 0 : Date.now();
    timeout = null;
    result = func.apply(context, args);
    if (!timeout) context = args = null;
  };
  return function() {
    var now = Date.now();
    if (!previous && options.leading === false) previous = now;
    var remaining = wait - (now - previous);
    context = this;
    args = arguments;
    if (remaining <= 0 || remaining > wait) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    } else if (!timeout && options.trailing !== false) {
      timeout = setTimeout(later, remaining);
    }
    return result;
  };
};

Обратите внимание, что этот код может быть упрощен, если вам не нужны все опции, которые подчеркивают поддержку.

Редактировать 1: Удалена еще одна ссылка на подчеркивание, спасибо комментарию Зеттама

Редактировать 2: Добавлено предложение о lodash и возможном упрощении кода, спасибо в комментарии lolzery wowzery

Как насчет этого?

function throttle(func, timeFrame) {
  var lastTime = 0;
  return function () {
      var now = new Date();
      if (now - lastTime >= timeFrame) {
          func();
          lastTime = now;
      }
  };
}

Просто.

Возможно, вам будет интересно взглянуть на источник.

обратный вызов: принимает функцию, которая должна быть вызвана

предел: количество раз, которое функция должна быть вызвана в течение срока

время: промежуток времени для сброса лимита

функциональность и использование: предположим, у вас есть API, который позволяет пользователю вызывать его 10 раз за 1 минуту

function throttling(callback, limit, time) {
    /// monitor the count
    var calledCount = 0;

    /// refesh the `calledCount` varialbe after the `time` has been passed
    setInterval(function(){ calledCount = 0 }, time);

    /// creating a clousre that will be called
    return function(){
        /// checking the limit (if limit is exceeded then do not call the passed function
        if (limit > calledCount) {
            /// increase the count
            calledCount++;
            callback(); /// call the function
        } 
        else console.log('not calling because the limit has exeeded');
    };
}
    
//////////////////////////////////////////////////////////// 
// how to use

/// creating a function to pass in the throteling function 
function cb(){
    console.log("called");
}

/// calling the closure funciton in every 100 miliseconds
setInterval(throttling(cb, 3, 1000), 100);

Добавление к обсуждению здесь (и для более недавних посетителей), если причина не использовать почти де-факто throttle от lodash должен иметь меньшую по размеру упаковку или комплект, то можно включить только throttle в вашем комплекте вместо всего lodash библиотека. Например, в ES6 это будет что-то вроде:

import throttle from 'lodash/throttle';

Также есть throttle только посылка из lodash называется lodash.throttle который может быть использован с простым import в ES6 или require в ES5.

Мне просто нужна была функция throttle/debounce для события изменения размера окна, и, любопытно, я также хотел знать, что это такое и как они работают.

Я читал несколько постов в блоге и тесты на SO, но все они, кажется, слишком усложняют это, предлагают библиотеки или просто предоставляют описания, а не простые реализации JS.

Я не буду давать описание, потому что это в изобилии. Итак, вот моя реализация:

function throttle(callback, delay) {
    var timeoutHandler = null;
    return function () {
        if (timeoutHandler == null) {
            timeoutHandler = setTimeout(function () {
                callback();
                clearInterval(timeoutHandler);
                timeoutHandler = null;
            }, delay);
        }
    }
}

function debounce(callback, delay) {
    var timeoutHandler = null;
    return function () {
        clearTimeout(timeoutHandler);
        timeoutHandler = setTimeout(function () {
            callback();
        }, delay);
    }
}

Это может потребовать настройки (например, изначально обратный вызов не вызывается сразу).

Посмотрите разницу в действии (попробуйте изменить размер окна):

function throttle(callback, delay) {
    var timeoutHandler = null;
    return function () {
        if (timeoutHandler == null) {
            timeoutHandler = setTimeout(function () {
                callback();
                clearInterval(timeoutHandler);
                timeoutHandler = null;
            }, delay);
        }
    }
}

function debounce(callback, delay) {
    var timeoutHandler = null;
    return function () {
        clearTimeout(timeoutHandler);
        timeoutHandler = setTimeout(function () {
            callback();
        }, delay);
    }
}

var cellDefault  = document.querySelector("#cellDefault div");
var cellThrottle = document.querySelector("#cellThrottle div");
var cellDebounce = document.querySelector("#cellDebounce div");

window.addEventListener("resize", function () {
    var span = document.createElement("span");
    span.innerText = window.innerWidth;
    cellDefault.appendChild(span);
    cellDefault.scrollTop = cellDefault.scrollHeight;
});

window.addEventListener("resize", throttle(function () {
    var span = document.createElement("span");
    span.innerText = window.innerWidth;
    cellThrottle.appendChild(span);
    cellThrottle.scrollTop = cellThrottle.scrollHeight;
}, 500));

window.addEventListener("resize", debounce(function () {
    var span = document.createElement("span");
    span.innerText = window.innerWidth;
    cellDebounce.appendChild(span);
    cellDebounce.scrollTop = cellDebounce.scrollHeight;
}, 500));
table {
    border-collapse: collapse;
    margin: 10px;
}
table td {
    border: 1px solid silver;
    padding: 5px;
}
table tr:last-child td div {
    width: 60px;
    height: 200px;
    overflow: auto;
}
table tr:last-child td span {
    display: block;
}
<table>
    <tr>
        <td>default</td>
        <td>throttle</td>
        <td>debounce</td>
    </tr>
    <tr>
        <td id="cellDefault">
            <div></div>
        </td>
        <td id="cellThrottle">
            <div></div>
        </td>
        <td id="cellDebounce">
            <div></div>
        </td>
    </tr>
</table>

JSFiddle

Вот как я реализовал функцию дроссельной заслонки в ES6 в 9LOC, надеюсь, это поможет

function throttle(func, delay) {
  let timeout = null
  return function(...args) {
    if (!timeout) {
      timeout = setTimeout(() => {
        func.call(this, ...args)
        timeout = null
      }, delay)
    }
  }
}

Нажмите на эту ссылку, чтобы увидеть, как это работает.

Я видел здесь много ответов, которые слишком сложны для «простого дросселя в js».

Почти все более простые ответы просто игнорируют вызовы, сделанные «на дросселе», вместо того, чтобы откладывать выполнение до следующего интервала.

Вот простая реализация, которая также обрабатывает вызовы «в режиме газа»:

      const throttle = (func, limit) => {
  let lastFunc;
  let lastRan = Date.now() - (limit + 1); //enforces a negative value on first run
  return function(...args) {
    const context = this;
    clearTimeout(lastFunc);
    lastFunc = setTimeout(() => {
      func.apply(context, args);
      lastRan = Date.now();
    }, limit - (Date.now() - lastRan)); //negative values execute immediately
  }
}

Это почти точная же реализация для простого дребезга. Он просто добавляет расчет задержки тайм-аута, который требует отслеживания, когда функция была запущена в последний раз. Смотри ниже:

      const debounce = (func, limit) => {
  let lastFunc;
  return function(...args) {
    const context = this;
    clearTimeout(lastFunc);
    lastFunc = setTimeout(() => {
      func.apply(context, args)
    }, limit); //no calc here, just use limit
  }
}

Вот дроссельная функция, идентичная vsync, с добавлением передачи аргументов в дроссельную функцию.

function throttle (callback, limit) {

  var wait = false;
  return function () {
    if (!wait) {

      callback.apply(null, arguments);
      wait = true;
      setTimeout(function () {
        wait = false;
      }, limit);
    }
  }
}

Можно использовать так:

window.addEventListener('resize', throttle(function(e){console.log(e)}, 100));

Используйте без контекста addEventListener:

throttle(function(arg1, arg2){console.log(arg1, arg2);}, 100)('red', 'blue');
// red blue

Простое решение в ES6. Демо-версия кода

Вот моя собственная версия поста Vikas:

throttle: function (callback, limit, time) {
    var calledCount = 0;
    var timeout = null;

    return function () {
        if (limit > calledCount) {
            calledCount++;
            callback(); 
        }
        if (!timeout) {
            timeout = setTimeout(function () {
                calledCount = 0
                timeout = null;
            }, time);
        }
    };
}

Я считаю, что с помощью setInterval не очень хорошая идея

С ведущими и конечными вызовами:

      const throttle = (fn, ms) => {
  let locked = false

  return function () {
    if (!locked) {
      locked = true
      fn.apply(this, arguments)

      setTimeout(() => {
        fn.apply(this, arguments)
        locked = false
      }, ms)
    }
  }
}

Прецедент:

      function log({ gender, address }) {
  console.log({
    name: this.name,
    gender,
    address,
  })
}

const jack = {
  name: 'Jack',
  log: throttle(log, 3000),
}

Array.from({ length: 5 }, () => jack.log({ gender: 'Male', address: 'LA' }))
function throttle(targetFunc, delay){
  let lastFunc;
  let lastTime;

  return function(){
    const _this = this;
    const args = arguments;

    if(!lastTime){
      targetFunc.apply(_this, args);
      lastTime = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(function(){
        targetFunc.apply(_this, args);
        lastTime = Date.now();
      }, delay - (Date.now() - lastTime));
    }
  }
}

Попытайся:

window.addEventListener('resize', throttle(function() {
  console.log('resize!!');
}, 200));

CodeSandbox

      const { now } = Date;

export default function throttle(func, frameDuration) {
  let timeout = null;
  let latest;
  const epoch = now();

  function getDurationToNextFrame() {
    const elapsed = now() - epoch;
    const durationSinceLastFrame = elapsed % frameDuration;
    return frameDuration - durationSinceLastFrame;
  }

  function throttled(...args) {
    latest = () => {
      func.apply(this, args);
    };
    if (!timeout) {
      timeout = setTimeout(() => {
        latest();
        timeout = null;
      }, getDurationToNextFrame());
    }
  }

  return throttled;
}

Здесь уже есть несколько отличных решений, но я искал современную версию с завершающими (и, возможно, ведущими) выполнениями, с последними переданными аргументами, предоставляемыми каждому вызову функции:

      const throttle = (fn, wait=500, leading=true) => {
  let prev, timeout, lastargs;
  return (...args) => {
    lastargs = args;
    if (timeout) return;
    timeout = setTimeout(() => {
      timeout = null;
      prev = Date.now();
      // let's do this ... we'll release the stored args as we pass them through
      fn.apply(this, lastargs.splice(0, lastargs.length));
      // some fancy timing logic to allow leading / sub-offset waiting periods
    }, leading ? prev && Math.max(0, wait - Date.now() + prev) || 0 : wait);
  };
}

Применение:

      x = throttle((...args) => console.log(...args));
let n = 0;
x(++n, 'boom');
x(++n, 'boom');
x(++n, 'boom');

Для этого есть библиотека, которая называется Backburner.js от Ember.

https://github.com/BackburnerJS/

Вы бы использовали это так.

var backburner = new Backburner(["task"]); //You need a name for your tasks

function saySomething(words) {
  backburner.throttle("task", console.log.bind(console, words)
  }, 1000);
}


function mainTask() {
  "This will be said with a throttle of 1 second per word!".split(' ').map(saySomething);
}

backburner.run(mainTask)

Я также хочу предложить простое решение, когда есть только одна функция, которую вы знаете, что вызовете (например: Поиск)

вот что я сделал в своем проекте

let throttle;

function search() {
    if (throttle) {
      clearTimeout(throttle);
    }
    throttle = setTimeout(() => {
      sendSearchReq(str)
    }, 500);
  }

Поиск вызывается при изменении ввода

Эта функция газа основана на ES6. Функция обратного вызова принимает аргументы (аргументы), и все же она работает, обернутая функцией газа. Будьте свободны, чтобы настроить время задержки в соответствии с потребностями вашего приложения. 1 раз на 100 мс используется для режима разработки, событие "oninput" является лишь примером частого случая его использования:

const callback = (...args) => {
  console.count('callback throttled with arguments:', args);
};

throttle = (callback, limit) => {
  let timeoutHandler = 'null'

  return (...args) => {
    if (timeoutHandler === 'null') {
      timeoutHandler = setTimeout(() => {            
        callback(...args)
        timeoutHandler = 'null'
      }, limit)
    }
  }
}

window.addEventListener('oninput', throttle(callback, 100));

PS Как объяснил @Anshul: регулирование обеспечивает максимальное количество вызовов функции с течением времени. Как в "выполнять эту функцию не чаще, чем раз в 100 миллисекунд".

Я сделал пакет npm с некоторыми функциями регулирования:

npm установить функцию-регулятор

throttleAndQueue

Возвращает версию вашей функции, которую можно вызывать максимум каждые W миллисекунд, где W - ожидание. Вызовы к вашему функционалу, которые происходят чаще, чем W, ставятся в очередь для вызова каждые W мс

throttledUpdate

Возвращает версию вашей функции, которую можно вызывать максимум каждые W миллисекунд, где W - ожидание. для вызовов, которые происходят чаще, чем W, последним будет вызов (последний имеет приоритет)

дроссель

ограничивает вызов вашей функции максимум каждые W миллисекунд, где W - ожидание. Звонки через W сбрасываются

В приведенном ниже примере попробуйте нажать кнопку несколько раз, но myFuncфункция будет выполняться только один раз в 3 секунды. Функцияthrottle передается с функцией, которая должна быть выполнена, и задержкой. Он возвращает закрытие, которое сохраняется в obj.throttleFunc. Теперь сobj.throttleFunc хранит закрытие, значение isRunning поддерживается внутри него.

function throttle(func, delay) {
  let isRunning;
  return function(...args) {
    let context = this;        // store the context of the object that owns this function
    if(!isRunning) {
      isRunning = true;
      func.apply(context,args) // execute the function with the context of the object that owns it
      setTimeout(function() {
        isRunning = false;
      }, delay);
    }
  }
}

function myFunc(param) {
  console.log(`Called ${this.name} at ${param}th second`);
}

let obj = {
  name: "THROTTLED FUNCTION ",
  throttleFunc: throttle(myFunc, 3000)
}

function handleClick() {
  obj.throttleFunc(new Date().getSeconds());
}
button {
  width: 100px;
  height: 50px;
  font-size: 20px;
}
    <button onclick="handleClick()">Click me</button>


Если мы не хотим, чтобы контекст или аргументы передавались, то более простой вариант этого будет следующим:

function throttle(func, delay) {
  let isRunning;
  return function() {
    if(!isRunning) {
      isRunning = true;
      func()
      setTimeout(function() {
        isRunning = false;
      }, delay);
    }
  }
}

function myFunc() {
  console.log('Called');
}


let throttleFunc = throttle(myFunc, 3000);

function handleClick() {
  throttleFunc();
}
button {
  width: 100px;
  height: 50px;
  font-size: 20px;
}
<button onclick="handleClick()">Click me</button>

Простая функция дроссельной заслонки -

Примечание. Продолжайте нажимать кнопку. Сначала вы увидите журнал консоли при нажатии, а затем только через каждые 5 секунд, пока вы не продолжите щелкать.

HTML -

<button id='myid'>Click me</button>

Javascript -

const throttle = (fn, delay) => {
  let lastTime = 0;
  return (...args) => {
      const currentTime = new Date().getTime();
      if((currentTime - lastTime) < delay) {
        return;
      };
      lastTime = currentTime;
      return fn(...args);
  }
};

document.getElementById('myid').addEventListener('click', throttle((e) => {
  console.log('I am clicked');
}, 5000));

Мы также можем реализовать с помощью флага -

var expensive = function(){
    console.log("expensive functionnns");
}

window.addEventListener("resize", throttle(expensive, 500))

function throttle(expensiveFun, limit){
    let flag = true;
    return function(){
        let context = this;
        let args = arguments;
        if(flag){
            expensiveFun.apply(context, args);
            flag = false;
            setTimeout(function(){
                flag = true;
            }, limit);
        }
    }
}

если будет более одной функции, определяющей их одну за другой, это будет невозможно поддерживать, поэтому я бы предложил использовать вспомогательный класс для хранения значений для каждого

Вот немного модернизированная и упрощенная версия ответа @clément-prévost

      function throttle(CB,ms=300,Id='Identifier for the callback(CB)'){
  Id = Id || ""+CB
  var N = throttle.N = throttle.N || {};  // Static variable N to store all callbacks ids and their status 
  if( N[Id] ) return;             // already in the queue to run 
  N[Id] = 1;                      // add it the queue 
  setTimeout(()=>{
    N[Id] = 0;                    // remove it from the queue
    CB();                         // finally call the function 
  }, ms);
}



for(var i=0;i<100;i++){
   throttle(e=>console.log("Hi1"),1e3,'F1');
}

// will only  output : Hi1
// this function guarantee the callback to run at least once 

Не нужно тонны локальных переменных для приличной функции газа. Целью функции регулирования является сокращение ресурсов браузера, а не применение таких дополнительных затрат, которые вы используете еще больше. В качестве доказательства этого утверждения я разработал дроссельную функцию, которая имеет только 4 "зависающие" переменные. (Переменная "зависания" - это переменная, которая никогда не собирается сборщиком мусора, потому что на нее всегда ссылается функция, которая потенциально может быть вызвана, тем самым впитывая память.) Горстка дроссельных функций обычно не приносит никакого вреда; но, если есть тысячи дросселированных функций, то памяти становится мало, если вы используете действительно неэффективную функцию дросселя. Мое решение ниже.

var timenow = self.performance?performance.now.bind(performance):Date.now;
function throttle(func, alternateFunc, minInterval) {
    var lastTimeWent = -1;
    return function() {
        var newTimeWent = timenow();
        if ((newTimeWent-lastTimeWent) > minInterval) {
            lastTimeWent = newTimeWent;
            return func.apply(this, arguments);
        } else if (typeof alternateFunc === "function")
            return alternateFunc.apply(this, arguments);
    };
}

Затем, чтобы обернуть эту функцию регулирования вокруг EventTarget для таких вещей, как щелчки DOM, события окна, XMLHttpRequests onprogress, FileReader onprogress и т. Д., Например, так:

var tfCache = []; // throttled functions cache
function listen(source, eventName, func, _opts){
    var i = 0, Len = tfCache.length, cF = null, options = _opts || {};
    a: {
        for (; i < Len; i += 4)
            if (tfCache[i] === func &&
              tfCache[i+1] === (options.ALTERNATE||null) &&
              tfCache[i+2] === (options.INTERVAL||200)
            ) break a;
        cF = throttle(func, options.ALTERNATE||null, options.INTERVAL||200);
        tfCache.push(func, options.ALTERNATE||null, options.INTERVAL||200, cF);
    }
    source.addEventListener(eventName, cF || tfCache[i+3], _opts);
    return cF === null; // return whether it used the cache or not
};
function mute(source, eventName, func, _opts){
    var options = _opts || {};
    for (var i = 0, Len = tfCache.length; i < Len; i += 4)
        if (tfCache[i] === func &&
          tfCache[i+1] === (options.ALTERNATE||null) &&
          tfCache[i+2] === (options.INTERVAL||200)
        ) {
            source.removeEventListener(eventName, tfCache[i+3], options);
            return true;
        }
    return false;
}

Пример использования:

(function(){"use strict";
// The function throttler //
var timenow = self.performance?performance.now.bind(performance):Date.now;
function throttle(func, alternateFunc, minInterval) {
    var lastTimeWent = -1;
    return function() {
        var newTimeWent = timenow();
        if ((newTimeWent-lastTimeWent) > minInterval) {
            lastTimeWent = newTimeWent;
            return func.apply(this, arguments);
        } else if (typeof alternateFunc === "function")
            return alternateFunc.apply(this, arguments);
    };
}
// The EventTarget wrapper: //
var tfCache = []; // throttled functions cache
function listen(source, eventName, func, _opts){
    var i = 0, Len = tfCache.length, cF = null, options = _opts || {};
    a: {
        for (; i < Len; i += 4)
            if (tfCache[i] === func &&
              tfCache[i+1] === (options.ALTERNATE||null) &&
              tfCache[i+2] === (options.INTERVAL||200)
            ) break a;
        cF = throttle(func, options.ALTERNATE||null, options.INTERVAL||200);
        tfCache.push(func, options.ALTERNATE||null, options.INTERVAL||200, cF);
    }
    source.addEventListener(eventName, cF || tfCache[i+3], _opts);
    return cF === null; // return whether it used the cache or not
};
function mute(source, eventName, func, _opts){
    var options = _opts || {};
    for (var i = 0, Len = tfCache.length; i < Len; i += 4)
        if (tfCache[i] === func &&
          tfCache[i+1] === (options.ALTERNATE||null) &&
          tfCache[i+2] === (options.INTERVAL||200)
        ) {
            source.removeEventListener(eventName, tfCache[i+3], options);
            return true;
        }
    return false;
}
// Finally, the key logger: //
var keysEle = document.getElementById("keyspressed");
var recordKeyStroke = function(dir,color){return function listener(Evt){
    if (Evt.key=="e") { mute(document, 'keydown', listener, downOptions);
                        mute(document, 'keyup', listener, upOptions); }
    if (!Evt.repeat) keysEle.insertAdjacentHTML(
        "afterbegin",
        '<div class="'+(Evt.key=="e"?"red":color)+'">'+dir+Evt.key+'</div>'
    );
}};
var downOptions = {passive:1, ALTERNATE: recordKeyStroke("+","grey") };
listen(document, 'keydown', recordKeyStroke("+","green"), downOptions);
var upOptions = {passive:1, ALTERNATE: recordKeyStroke("-","grey") };
listen(document, 'keyup',   recordKeyStroke("-","green"), upOptions);
})();
The keys you press and release are shown below.
Those in grey are ones which were passed up by the throttle.
Those in green are ones that were recorded by the throttle.
When the "e" key is pressed, it is red and keystrokes are no longer recorded.
"+[key]" = keydown and "-[key]" = keyup.
<div id="keyspressed" style="white-space:pre-wrap;font-family:'Roboto Mono',monospace;"></div>
<style>.red{color:#f77}.green{color:#5f5}.grey{color:#aaa}</style>

По умолчанию это ограничивает функцию до одного вызова каждые 200 мс. Чтобы изменить интервал на другое количество миллисекунд, затем передайте optionsObject.INTERVAL параметр в аргументе параметров и установите его на желаемое минимальное количество миллисекунд между выполнениями. (Поскольку таймеры не всегда самые точные,) Если у вас есть точный минимальный желаемый интервал, то я бы порекомендовал вам вычесть один или два из желаемых optionsObject.INTERVAL чтобы он всегда выполнялся, по крайней мере, когда должен. Если вам нужно что-то сделать с аргументами функции throttled, когда выполнение функции throttled задерживается (из-за избыточных вызовов), используйте команду optionsObject.ALTERNATE вариант. Этот "ALTERNATE" является функцией, которая вызывается немедленно вместо основной функции всякий раз, когда прекращается вызов основной функции. Например, если вы используете функцию удушения в EventTarget, но хотите preventDefault() на пропущенных событиях, затем используйте {ALTERNATE: function(evt){ evt.preventDefault(); }} для объекта параметров.

Ниже приведен самый простой газ, о котором я только мог подумать, в 13 LOC. Он создает тайм-аут при каждом вызове функции и отменяет старую. Исходная функция вызывается с правильным контекстом и аргументами, как и ожидалось.

function throttle(fn, delay) {
  var timeout = null;

  return function throttledFn() {
    window.clearTimeout(timeout);
    var ctx = this;
    var args = Array.prototype.slice.call(arguments);

    timeout = window.setTimeout(function callThrottledFn() {
      fn.apply(ctx, args);
    }, delay);
  }
}

// try it out!
window.addEventListener('resize', throttle(function() {
  console.log('resize!!');
}, 200));

Другие вопросы по тегам