Js中的调节器:提高应用程序的性能

时间: 2019-12-09阅读: 268标签: 性能

调节器是浏览器中通过限制代码要处理的事件数量来提高性能的常用技术。当你想以受控的速率执行回调时,应该使用调节器,它允许你在每个固定的时间间隔内重复处理过渡状态。

我将以一个真实世界的类比开始,然后在 Web 上下文中描述调节器,最后提供有关如何实现节流的注释代码示例。在文章的结尾,有一个带有调节器示例的 Codepen,你可以与之交互以查看其工作原理。如果只关心代码,请跳至 “JavaScript 中的调节器实现” 部分。

调节器是“去抖动” 的表亲,它们都可以提高 Web 应用的性能。但是它们在不同的情况下使用。当你只关心最终状态时,会使用去抖功能。例如等待用户停止键入以获取预先输入的搜索结果。当你想要以受控的速率处理所有中间状态时,最好使用调节器。例如,当用户调整窗口大小并在页面内容改变时重新排列页面内容时跟踪屏幕宽度,而不是等到用户完成操作时再跟踪。


真实世界中调节器的例子

一个比喻是我们的饮食方式。我们想节制饮食,以便每 6 小时吃一顿饭。我们早上 7 点起床吃早餐,然后节流,直到下午 1 点吃午餐,最后在晚上 7 点吃晚餐。每次吃完饭后,我们就会阻止自己进食 6 个小时,以确保整天都能以合理的增量获得食物。

这种类比可以扩展到生活中以设定的增量去执行动作的任何情形。例如,我们希望每三个月更换一次汽车中的机油。我们不会提前这样做,因为那是在浪费金钱,我们也不会拖延,因为这会损坏汽车引擎。我们会检查挡风玻璃上的贴纸,看是否经过了足够的时间,然后我们去找机械师。因此,我们会每 3 个月就进行一次换油,这样可以最有效地处理换油事件。


Web 开发中的节流

为了理解 Web 开发上下文中的限制,假设你有一个滚动事件处理程序,当用户在页面上向下移动时,你想在其中向用户显示新内容。如果在每次用户滚动单个像素时都执行回调,假如快速滚动的话,我们将会很快就被事件阻塞,因为它将快速连续发送数百或数千个事件。相反,我们对其进行限制,仅每 100 毫秒检查一次滚动,这样每秒仅获得10个回调。用户仍然可以立即感觉到响应,但是计算效率更高。

调节器用于创建均匀间隔的函数调用。想象一下,如果你在事件处理程序回调函数中执行大量计算或 API 请求。通过限制这些回调,可以防止应用冻结或对服务器发出不必要地请求。


JavaScript 中的调节器的实现

让我们立即进入调节器代码。我会在下面进行描述,然后提供该功能的注释版本。

const throttle = (callback, delay) => {
  let throttleTimeout = null;
  let storedEvent = null;

  const throttledEventHandler = event => {
    storedEvent = event;

    const shouldHandleEvent = !throttleTimeout;

    if (shouldHandleEvent) {
      callback(storedEvent);

      storedEvent = null;

      throttleTimeout = setTimeout(() => {
        throttleTimeout = null;

        if (storedEvent) {
          throttledEventHandler(storedEvent);
        }
      }, delay);
    }
  };

  return throttledEventHandler;
};
这个调节器的实现是最简单易懂的。它仅用于教学目的,并非是效率最高或代码行数最少的。

调节器是一个高阶函数,这是一个返回另一函数的函数(为清楚起见,此处命名为throttledEventHandler)。这样做是为了围绕 callback、delay、throttleTimeout 和 storedEvent 参数形成一个闭包。这保留了在执行 throttledEventHandler 时要读取的每个变量的值。以下是每个变量的定义:

  • callback:你想要以给定速率执行的节流函数。
  • delay:你希望节流函数在多次执行 callback 之间等待的时间。
  • throttleTimeout: The value used to indicate a running throttle created by our setTimeout.
  • throttleTimeout:该值用于指示由 setTimeout 创建的调节器。
  • storedEvent:你想通过节流 callback 处理的事件。该值将不断更新,直到截流结束。

我们可以在以下代码中使用调节器:

var returnedFunction = throttle(function() {
  // Do all the taxing stuff and API requests
}, 500);

window.addEventListener('scroll', returnedFunction);

由于调节器返回一个函数,因此第一个例子中的 throttledEventHandler 和第二个例子中的 returnedFunction 函数实际上是相同的函数。每次用户滚动鼠标时,它将执行 throttledEventHandler/returnedFunction。

下面逐步说明在截流函数时会发生什么。首先,我们围绕变量创建一个闭包,以便每次执行时它们都可用于ThrottledEventHandler。 ThrottledEventHandler 接收到 1 个作为事件的参数。它将事件存储在 storedEvent 变量中。

然后检查运行是否超时(即激活调节器)。如果调节器生效,那么 throttledEventHandler 已经完成了该执行并等待执行回调。如果调节器为非活动状态,则可以用回调函数立即处理该事件。然后调用 setTimeout 并存储超时值,该值表明调节器正在生效。

当 timeout 处于活动状态时,将始终存储最新事件。这时则会跳过回调的执行,这可以使我们免于执行 CPU 密集型任务或调用我们的 API。

当 setTimeout 结束时,将 throttleTimeout 置为空,这表明该函数不再受到限制并且可以处理事件。如果有一个 storedEvent,我们想立即处理它,这是则会递归地调用 throttledEventHandler。 setTimeout内部的递归调用使我们能够以恒定的速率处理事件。只要有新事件继续发生,它就会在期望的延迟后重复执行相同的处理过程。

该函数的注释版本:

// 传递我们要限制的回调以及限制事件之间的延迟
const throttle = (callback, delay) => {
  // 在这些变量周围创建一个闭包。
  // 它们将在调节器处理的所有事件之间共享。
  let throttleTimeout = null;
  let storedEvent = null;

  // 当调节器处于活动状态时,此函数将处理事件和调节器回调。
  const throttledEventHandler = event => {
    // 每次迭代都更新存储的事件
    storedEvent = event;

    // 如果调节器尚未激活,我们将使用事件执行回调
    const shouldHandleEvent = !throttleTimeout;

    // 如果没有活动的调节器,将执行回调并创建一个新的调节器。
    if (shouldHandleEvent) {
      // 处理我们的事件
      callback(storedEvent);

      // 由于我们使用了已存储的事件,因此将其清空。
      storedEvent = null;

      // 通过设置超时来创建新的限制,以防止在延迟期间处理事件。
      // 超时结束后,如果有存储的事件,则执调节器。
      throttleTimeout = setTimeout(() => {
        // 由于调节器时间已到期,因此我们立即使调节器超时无效。
        throttleTimeout = null;

        // 如果我们有一个存储的事件,则递归调用此函数。
        // 递归使我们能够在事件发生时连续运行。
        // 如果事件停止了,我们的调节器将结束。 如果有新事件发生,它将立即执行。
        if (storedEvent) {
          // 由于超时结束:
          // 1. 由于节流时间现在为 null,因此本递归调用将立即执行 `callback`
          // 2. 它将重新启动调节器 timer,使我们可以重复调节器过程
          throttledEventHandler(storedEvent);
        }
      }, delay);
    }
  };

  // 返回受限制的事件处理作为闭包
  return throttledEventHandler;
};

互动示例:https://codepen.io/


总结

对于 JavaScript 开发人员而言,节流是一个非常重要且有益的概念。它是提高 Web 应用性能的常用工具,从头开始实施节流功能还可以增强你的高级 JS 技术,例如闭包、异步事件处理、高阶函数和递归。

作者:Trey Huffine
翻译:疯狂的技术宅
原文:https://levelup.gitconnected.com/


站长推荐

1.阿里云: 本站目前使用的是阿里云主机,安全/可靠/稳定。点击领取2000元代金券、了解最新阿里云产品的各种优惠活动点击进入

2.腾讯云: 提供云服务器、云数据库、云存储、视频与CDN、域名等服务。腾讯云各类产品的最新活动,优惠券领取点击进入

3.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/7213

测量JavaScript函数的性能的简单方法及与其他方式对比

测量执行一个函数所需的时间总是一个很好的办法,证明某些实现比另一个实现的性能更好。这也是一个很好的方法,可以确保性能没有在某些改变后受到影响,也可以追踪瓶颈。良好的性能有助于获得良好的用户体验

怎样测试 JavaScript 的函数性能

通过衡量执行某个函数所花费的时间,以“证明”某些实现比另一些实现更高效始终是一个很好的主意。这也是确保性能在进行一些修改后不受影响并找出瓶颈的好方法。良好的性能有助于获得良好的用户体验。良好的用户体验能够留住用户

如何优化css expression性能?

IE 浏览器中 CSS Expression 特性的最大的问题:会反复执行,每秒钟可能执行了成百上千次,有严重的性能问题。何对 CSS Expression 进行优化呢?至少:如果我们将 CSS Expression 在匹配的元素中仅执行一次,性能将会提升很大。

vue + webpack 前端性能优化

对于程序开发者而言,开发一个项目不仅仅注重效率和功能,前端的性能问题也是非常重要的。这直接影响用户的体验,从而间接的也反应该项目质量的好坏

小程序Canvas性能优化实战

在小程序中使用canvas组件绘制地铁图,地铁图包括地铁线路、站点图标、线及站点名称文字,绘制元素为线、圆、图片、文字。 支持拖动平移和双指缩放。

让你的 React 组件跑得再快一点

React 基于虚拟 DOM 和高效 Diff 算法的完美配合,实现了对 DOM 最小粒度的更新。大多数情况下,React 对 DOM 的渲染效率足以我们的业务日常。但在个别复杂业务场景下,性能问题依然会困扰我们。此时需要采取一些措施来提升运行性能

Java与Node.js性能PK

如果你打开浏览器,搜索“Java与Node.js哪个更快”,你会发现大部分答案声称Node.js更快,也有一些人持相反意见。Java使用JIT编译器,其性能甚至可以超过C++。在这种情况下,为什么这么多人还是声称Node.js要比Java快呢?

React性能优化整理

通过判断减少数据变化触发的重新渲染, 以及之后的 DOM diff;函数式语言当中, 语言设计允许两个对象一样, 举例 Clojure:;每个函数体当中生成的对象都会有新的引用, useMemo 可以保留一致的引用.

大型php网站性能和并发访问优化方案

网站性能优化对于大型网站来说非常重要,一个网站的访问打开速度影响着用户体验度,网站访问速度慢会造成高跳出率,小网站很好解决,那对于大型网站由于栏目多,图片和图像都比较庞大,那该怎么进行整体性能优化呢

优化 web 应用程序性能方案总结

在开发 web 应用程序时候,性能都是必不可少的话题。而大部分的前端优化机制都已经被集成到前端打包工具 webpack 中去了,当然,事实上仍旧会有一些有趣的机制可以帮助 web 应用进行性能提升

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!

文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全运营推广