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

更新日期: 2019-12-09阅读: 1.7k标签: 性能

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

我将以一个真实世界的类比开始,然后在 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/


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

提高js加载速度,实现js无阻塞加载方式,高性能的加载执行JavaScript

为解决JS加载速度慢,采用js的延时加载,和动态加载。由于js的堵塞特性,当浏览器在加载javascript代码时,不能同时做其他任何事情,如果javascript执行时间越久,浏览器等待响应的时间就越久。

如何提高CSS性能?CSS优化、提高性能提升总汇

如何提高CSS性能,根据页面的加载性能和CSS代码性能,主要表现为: 加载性能 (主要是从减少文件体积,减少阻塞加载,提高并发方面入手),选择器性能,渲染性能,可维护性。

前端性能优化_css加载会造成哪些阻塞现象?

css的加载是不会阻塞DOM的解析,但是会阻塞DOM的渲染,会阻塞link后面js语句的执行。这是由于浏览器为了防止html页面的重复渲染而降低性能,所以浏览器只会在加载的时候去解析dom树,然后等在css加载完成之后才进行dom的渲染以及执行后面的js语句。

2018 前端性能检查表

性能十分重要。然而,我们真的知道性能瓶颈具体在哪儿吗?是执行复杂的 JavaScript,下载缓慢的 Web 字体,巨大的图片,还是卡顿的渲染?研究摇树(Tree Shaking),作用域提升(Scope Hoisting)

高性能Javascript总结

Js高性能总结:加载和运行、数据访问、DOM编程、算法和流程控制、响应接口、Ajax 异步JavaScript和XML、编程实践...

优化网站性能规则_前端性能优化策略【网络加载、页面渲染】

前端网站性能优化规则:网络加载类、页面渲染类。包括:减少 HTTP 资源请求次数、减小 HTTP 请求大小、避免页面中空的 href 和 src、合理设置 Etag 和 Last-Modified、使用可缓存的 AJAX、减少 DOM 元素数量和深度等

前端性能的本质是什么?

性能一直以来是前端开发中非常重要的话题。随着前端能做的事情越来越多,浏览器能力被无限放大和利用:从 web 游戏到复杂单页面应用,从 NodeJS 服务到 web VR/AR 和数据可视化,前端工程师总是在突破极限

BigPipe_高性能流水线页面技术

BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器和浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。

用CSS开启硬件加速来提高网站性能

你知道我们可以在浏览器中用css开启硬件加速,使GPU (Graphics Processing Unit) 发挥功能,从而提升性能吗?现在大多数电脑的显卡都支持硬件加速。鉴于此,我们可以发挥GPU的力量,从而使我们的网站或应用表现的更为流畅。

原生js实现懒加载并节流

像淘宝网站等,页面中有着大量图片,一次性全部加载这些图片会使浏览器发送大量请求和造成浪费。采用懒加载技术,即用户浏览到哪儿,就加载该处的图片。这样节省网络资源、提升用户体验、减少服务器压力。

点击更多...

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