「防抖与节流 」每个请求必须发送,平滑地获取最后一个返回值

编程学思 2019-11-06 13:20:04 ⋅ 600 阅读
作者:krryblog博客地址:https://ainyi.com/79


日常浏览网页中,在进行窗口的 resize、scroll 或者重复点击某按钮发送请求,此时事件处理函数或者接口调用的频率若无限制,则会加重浏览器的负担,界面可能显示有误,服务端也可能出问题,导致用户体验非常糟糕

此时可以采用 debounce(防抖)和 throttle(节流)的方式来减少事件或接口的调用频率,同时又能实现预期效果

防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发

节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数

区别:函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在连续触发的事件后才触发最后一次事件的函数

上面的解释,摘抄网上的解答

防抖

debounce:当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时

如下图,持续触发 scroll 事件时,并不执行 handle 函数,当 1000ms 内没有触发 scroll 事件时,才会延时触发 scroll 事件


function debounce(fn, wait) {
let timeout = null
return function() {
if(timeout !== null) {
clearTimeout(timeout)
}
timeout = setTimeout(fn, wait)
}
}
// 处理函数
function handle() {
console.log('处理函数', Math.random())
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000))

节流

throttle:当持续触发事件时,保证一定时间段内只调用一次事件处理函数

仔细了解了才知道,我以前刚学前端的时候,做 banner 图特效,两边的点击按钮如果一直重复点击就会出问题,后面摸索了此方法,原来这名字叫做节流

如下图,持续触发 scroll 事件时,并不立即执行 handle 函数,每隔 1000 毫秒才会执行一次 handle 函数


时间戳方法

let throttle = function(func, delay) {
let prev = Date.now()
return function() {
let context = this
let args = arguments
let now = Date.now()
if(now - prev >= delay) {
func.apply(context, args)
prev = Date.now()
}
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', throttle(handle, 1000))

定时器方法

let throttle = function(func, delay) {
let timer = null
return function() {
let context = this
let args = arguments
if(!timer) {
timer = setTimeout(function() {
func.apply(context, args)
timer = null
}, delay)
}
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', throttle(handle, 1000))

时间戳+定时器

let throttle = function(func, delay) {
let timer = null
let startTime = Date.now()
return function() {
let curTime = Date.now()
let remaining = delay - (curTime - startTime)
let context = this
let args = arguments
clearTimeout(timer)
if(remaining <= 0) {
func.apply(context, args)
startTime = Date.now()
} else {
timer = setTimeout(func, remaining)
}
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', throttle(handle, 1000))

每个请求必须发送的问题

如下图的购买页,操作发现一个购买明细的查价接口的频繁调用问题

如下图:


购买页改变任何一个选项,都会调用查价接口,然后右边会显示对应的价格。尤其是购买数量,这是一个数字选择器,如果用户频繁点击 + 号,就会连续调用多次查价接口,但==最后一次的查价接口返回的数据才是最后选择的正确的价格==

每个查价接口逐个请求完毕的时候,==右边的显示价格也会逐个改变==,最终变成最后正确的价格,一般来说,这是比较不友好的,用户点了多次后,不想看到价格在变化,尽管最终是正确的价格,但这个变化的过程是不能接受的

也不应该使用上面的防抖解决方式,不能设置过长的定时器,因为查价接口不能等太久,也不能设置过短的定时器,否则会出现上面说的问题(价格在变化)

所以这是一个==每个请求必须发送,但是只显示最后一个接口返回的数据的问题==

我这里采用入栈、取栈顶元素比对请求参数的方法解决:

// 查价
async getPrice() {
// 请求参数
const reqData = this.handleData()
// push 入栈
this.priceStack.push(reqData)
const { result } = await getProductPrice(reqData)
// 核心代码,取栈顶元素(最后请求的参数)比对
if(this.$lang.isEqual(this.$array.last(this.priceStack), reqData)) {
// TODO
// 展示价格代码...
}
}

注解,上述的 this.$lang.isEqual、this.$array.last 均是 lodash 插件提供的方法

注册到 Vue 中

import array from 'lodash/array'
import Lang from 'lodash/lang'
Vue.prototype.$array = array
Vue.prototype.$lang = Lang



全部评论: 0

    我有话说:

    Android 12 最后一个开发者预览版发布

    谷歌已经发布了 Android 12 的第三个开发者预览版,这也是最后一个以开发者为中心的构建版本。第一个 Android 12 测试版将在 5 月到来,第二个和第三个测试版构建则将在 6 月至 7

    FreeFileSync 11.3 发布,文件夹比较同步软件

    FreeFileSync 11.3 已发布。这是一个文件夹比较和同步软件,可以创建和管理所有重要文件的备份副本。FreeFileSync 不是每次都复制每个文件,而是确定源文件夹目标文件夹之间的

    FreeFileSync 11.4 发布,文件夹比较同步软件

    FreeFileSync 11.4 已发布。这是一个文件夹比较和同步软件,可以创建和管理所有重要文件的备份副本.FreeFileSync 不是每次都复制每个文件,而是确定源文件夹目标文件夹之间的

    FreeFileSync 11.5 发布,文件夹比较同步软件

    FreeFileSync 11.5 已发布。这是一个文件夹比较和同步软件,可以创建和管理所有重要文件的备份副本.FreeFileSync 不是每次都复制每个文件,而是确定源文件夹目标文件夹之间的

    FreeMarker使用模板生成HTML静态页面

    哈喽,最近的项目中正好在做一个发布新闻的功能,每个新闻可能要有不同的蚊页面喽~

    「开源资讯」Atom 1.52.0 和1.53.0-beta0发布,跨平台文本编辑器

    Atom 同时发布了 1.52.0 和 1.53.0-beta0 版本。Atom 是 GitHub 专门为程序员推出的一个平台文本编辑器。具有简洁和直观的图形用户界面,并有很多有趣的特点:支持

    微信小程序音实战-首页(下)

    音小程序首页动态数据获取

    PrettyZoo V1.7.0,高颜 Zookeeper 桌面工具

    PrettyZoo 是一款基于 JavaFX 实现的 Zookeeper 桌面图形化工具,不仅颜极高,而且功能齐全,支持 Windows / Mac / Linux 等主流平台。 这次更新增加了

    从你在浏览器输入url到发起http请求,这过程到底发生了什么?

    当你在浏览器输入url到发起http请求,这过程到底发生了什么?

    微信小程序:最新微信登录授权并获取openid等信息

    为优化用户体验,使用 wx.getUserInfo 接口直接弹出授权框的开发方式将逐步不再支持。从2018年4月30日开始,小程序小游戏的体验版、开发版调用 wx.getUserInfo 接口,将

    【插件】TinyMCE 选择一个图片用作封面

      TinyMCE版本 4.7.6 思路:在imagetools 的基础上增加一个按钮,点击之后返回当前图片节点 注意!!黄色背景的内容为增加的内容 》 准备阶段 去官网下载

    Google技术:你不了解Google最新发布的JS代码规范 的最佳实践

    Google为了那些还不熟悉代码规范的人发布一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。

    微信小程序音实战-首页(上)

    你也可以用微信小程序编写一个

    PowerJob v3.3.2 已经发布,分布式任务调度中间件

    PowerJob v3.3.2 已经发布,PowerJob 是全新一代分布式调度计算框架,能让您轻松完成作业的调度繁杂任务的分布式计算。 此版本更新内容包括: Features 支持控制台查看

    Java Web实战篇:发布和运维必备的12条Linux命令

    作为一名Java起步的从业人员,学会一些常用的Linux命令是必须的。

    音 APP 性能优化系列:Java 内存优化篇

    内存作为计算机程序运行最重要的资源之一,需要运行过程中做到合理的资源分配回收,不合理的内存占用轻则使得用户应用程序运行卡顿、ANR、黑屏,重则导致用户应用程序发生 OOM(out of

    老板要我开发一个简单的工作流引擎

    第1关 一天,老板找到我,说要做个简单的工作流引擎。 我查了一天啥是工作流,然后做出了如下版本: 按顺序添加任意个审批人组成一个链表,最后一个结束节点 记录当前审批人,当审批完后,审批人向后

    Nginx Unit 1.21.0 发布,动态 Web 应用服务器

    Nginx Unit 1.21.0 已发布,更新内容除了常规的 bugfix 外,还增加了部分新特性,例如支持条件匹配的 PCRE 和多线程请求处理等。 因此,开发者现在可以调整每个应用程序进程中

    jQuery 3.6.0 发布,即使存在 JSONP 错误也返回 JSON

    jQuery 是一个快速、小型且功能丰富的 JavaScript 库。通过易于使用的 API(可在多种浏览器中使用),使 HTML 文档的遍历和操作、事件处理、动画和 Ajax 等操作变得更加简单