JavaScript中的定时控制-Throttle、Debounce、Immediate的API实现

javascript1

上一篇文章中,我们研究了一些技术用来函数的定时控制(回调),在特定事件发生时触发。这些技术是:

  • Throttle
  • Debounce
  • Immediate

在这篇文章中,我们将看到这些技术如何以高阶函数的形式实现通用的API。

我们首先看看 Debounce 的实现:

var delta = 1000;
var timeoutID = null;

function foo() {
  console.log('bar');
}

function debouncedFoo() {
  clearTimeout(timeoutID);
  timeoutID = setTimeout(function() {
    foo();
  }, delta);
};

window.onkeydown = debouncedFoo;

这个实现有几个问题。

1. 传递参数(Passing arguments)

首先,如果foo期望传递一些参数?在调用时,我们需要提供这些参数。这个问题可以通过添加参数到debouncedFoo来解决。

function foo(a, b, c) {
  // ...
}

function debouncedFoo(x, y, z) {
  // ...
    foo(x, y, z);
  // ...
};

2.未知参数数量的函数

Arity是函数接受参数的数量。如果我们知道 foo 需要几个参数,上面的代码就可以很好的工作。在函数未知参数数量的情况下,我们可以使用 arguments对象

我们将使用.apply方法,因为它允许函数执行时将参数作为数组或类数组对象(例如:arguments)传递。

function foo(a, b, c) {
  // ...
}

function debouncedFoo(arguments) {
  var args = arguments;
  // ...
    foo.apply(null, args);
  // ...
};

多个 debounced 函数

现在,让我们继续下一个问题 – 生产。我们需要多少个 debounced 函数?我们要为他们硬编码吗?

更好的方法是使用高阶函数。在HOF(高阶函数)上有大量的资源,我建议你好好阅读这部分的内容。简单来说,HOF(高阶函数)接受一个函数并返回另一个函数。

var delta = 1000;

function log(e) {
  console.log(e);
}

function debounce(fn, delta) {
  var timeoutID = null;

  return function() {
    clearTimeout(timeoutID);

    var args = arguments;
    timeoutID = setTimeout(function() {
      fn.apply(null, args);
    }, delta);
  };
}

var debouncedLog = debounce(log, delta);
window.onkeydown = debouncedLog;

防止上下文丢失

到现在为止,我们可以将任意数量的函数转换为自身的 debounce 版本,以及传递参数。但是还有另一个问题 – 上下文丢失。

因为我们使用了函数的.apply方法,我们为函数调用提供上下文(也叫做 this 变量)。对于内部依赖 this 的任何函数都是一个问题。

为了解决这个问题,我们需要的是 debounce 函数正确的上下文。但是我们不能从函数本身提取上下文。因此,它必须从外部提供。

// ...

function debounce(fn, delta, context) {
  // ...
      fn.apply(context, args);
  // ...
}

结论

最后,我们得到的 Debounce 代码:

See the Pen debounce by feiwen8772 (@feiwen8772) on CodePen.0

或者

See the Pen debounce 2 by feiwen8772 (@feiwen8772) on CodePen.0

Debounce

function debounce(fn, delta, context) {
  var timeoutID = null;

  return function() {
    clearTimeout(timeoutID);

    var args = arguments;
    timeoutID = setTimeout(function() {
      fn.apply(context, args);
    }, delta);
  };
}

其余的技术:

Immediate

function immediate(fn, delta, context) {
  var timeoutID = null;
  var safe = true;

  return function() {
    var args = arguments;

    if (safe) {
      fn.call(context, args);
      safe = false;
    }

    clearTimeout(timeoutID);
    timeoutID = setTimeout(function() {
      safe = true;
    }, delta);
  };
}

Throttle

function throttle(fn, delta, context) {
  var safe = true;

  return function() {
    var args = arguments;

    if (safe) {
      fn.call(context, args);

      safe = false;
      setTimeout(function() {
        safe = true;
      }, delta);
    }
  };
}

这里有一个关于所有三种技术的小演示:

See the Pen Debounce Immediate Throttle by feiwen8772 (@feiwen8772) on CodePen.0

在下一篇文章,也是这个系列文章中的最后一篇文章,我们将看到一个Throttle的本地实现,可以很好地在浏览器中工作。

敬请关注。

翻译自:Timing Controls – Part 2

赞(0) 打赏
未经允许不得转载:WEB前端开发 » JavaScript中的定时控制-Throttle、Debounce、Immediate的API实现

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #-49

    很赞的文章!按照楼主的文章和demo,总结一下:当有持续性动作的时候,Throttle 降低频率持续性触发,而 Debounce 和 Immediate 只会触发一次,且 Debounce 在动作之后触发,Immediate 在动作之前触发

    时间被海绵吃了2年前 (2016-11-17)回复

前端开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏