在Javascript中,如果函数已经在执行,则对函数的执行进行排队,但取消之前排队的任何函数

In Javascript, queuing the execution of function if the function is already executing, but cancel any previously queued

本文关键字:函数 执行 排队 取消 任何 Javascript 如果      更新时间:2023-09-26

我经常遇到以下情况,所以我想知道是否有内置的jQuery方法来解决这个问题。

想象一下以下代码:

$(document).click(function() {
   paintCanvas();
});

这个代码的问题是,如果用户连续快速点击屏幕50次,你将用50次对paintCanvas的调用来重载浏览器。

如果paintCanvas当前正在执行,并且创建了一个新请求,我们希望对新请求进行排队,以便它等待paintCan画布完成执行。然而,同时,我们可以放弃任何先前排队的对paintCanvas的调用,因为我们只关心鼠标的最终状态,而不是所有的中间状态。

以下是一些解决问题的代码:

var _isExecuting, _isQueued;
function paintCanvas() {
    if (_isExecuting) {
        if (!_isQueued) {
            _isQueued = true;
            setTimeout(function() {
                _isQueued = false;
                paintCanvas();
            }, 150);
        }
        return;
    }
    _isExecuting = true;
    // ... code goes here
    _isExecuting = false;
};

这个AJAX队列插件本质上实现了这个功能,但它只是在AJAX方面实现的。这肯定是一个非常常见的问题,可以用更通用的方式解决吗?

您不应该用mousemove来解决这个问题,因为系统已经为您解决了。在执行paintCanvas时,即使鼠标剧烈移动,它也不会生成数百个鼠标移动事件。相反,下一个事件将是鼠标的当前位置,而不是所有介入鼠标事件的队列。

看看这个jsFiddle:http://jsfiddle.net/jfriend00/4ZuMn/.

在身体(右下方窗格)中快速摆动鼠标。然后将鼠标移出窗格,注意计数立即停止——不再有鼠标事件。它永远不会堆积鼠标事件。每当系统为下一次鼠标事件做好准备时,它就会获取鼠标的最新位置。单个鼠标移动不会排队-它们不会累积。您还可以在鼠标事件列表中看到,即使鼠标经过了更多的位置,也没有出现许多插入的鼠标事件(例如,缺少许多坐标)。这是因为当鼠标处于该位置时,系统还没有准备好生成鼠标事件,因此跳过了该位置。

此外,因为javascript是单线程的,所以在当前处理鼠标事件时,您永远不会得到新的鼠标事件。系统不会生成一个新的,直到你处理完你已经是一个了。因此,您永远不会在代码中的javascript中将_isExecuting看作true。你根本不需要那张支票。而且,由于您不需要该检查,而且它永远不会是真的,因此您的任何排队代码都不会执行。您可以在这个jsFiddle中看到,您永远无法捕获重新输入的mousemove事件:http://jsfiddle.net/jfriend00/ngnUT/.inAction标志永远不会被认为是真的,无论你移动鼠标的速度有多快或有多大。

听起来你想要节流/防反弹功能。

据我所知,jQuery中没有内置方法,但您可以使用其中任何一种:

http://benalman.com/projects/jquery-throttle-debounce-plugin/

http://jsperf.com/jquery-throttle-methods

虽然@rkw提供了一个链接,但我总是喜欢在SO上显示代码。返回另一个函数的缓冲版本的函数。这将持续延迟,直到它停止接收给定delay的事件。如果您不想在最后一个事件之后等待延迟,可以对此进行调整。您所需要做的就是跟踪第一次设置超时的时间,并偏移对setTimeout的后续调用。

下面是一个工作示例http://jsfiddle.net/mendesjuan/qfFjZ/

function createBuffered(handler, delay) {
  var timeoutId = null;
  return function() {
     var me = this;
     if (timeoutId) {
        window.clearTimeout(timeoutId);
     }
     timeoutId = setTimeout(function() {
         handle.apply(me, arguments);
         timeoutId = null;
     }, delay);
  }
}