防抖与节流
介绍
通过防抖(debounce) 和 节流(throttle) 的方式来减少调用频率,减少资源浪费 防抖 : n 秒后再执行该事件,若在 n 秒内被重复触发 ,则重新计时
(用搭电梯形容:电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖)节流 : n 秒内只运行一次,若在 n 秒内重复触发 ,只有一次生效
(用搭电梯形容:电梯第一个人进来后,15秒后准时运送一次,这是节流)
区别
相同点
都可以通过使用 setTimeout 实现 目的都是,降低回调执行频率。节省计算资源
不同点
函数防抖,在一段连续操作结束后 ,处理回调,利用clearTimeout和 setTimeout实现 。函数节流,在一段连续操作中,每一段时间只执行一次 ,频率较高的事件中使用来提高性能 函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次
使用场景
防抖在连续的事件 ,只需触发一次回调的场景有:
搜索框搜索输入。只需用户最后一次输入完,再发送请求 手机号、邮箱验证输入检测 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染
节流在间隔一段时间执行一次回调 的场景有:
滚动加载,加载更多或滚到底部监听 搜索框,搜索联想功能
具体实现
防抖
function debounce ( func, wait ) {
let timeout;
return function ( ) {
let self = this ;
let args = arguments;
clearTimeout ( timeout) ;
timeout = setTimeout ( function ( ) {
func . apply ( self, args) ;
} , wait) ;
}
}
function debounce ( func, wait, immediate ) {
let timeout;
return function ( ) {
let self = this ;
let args = arguments;
if ( timeout) clearTimeout ( timeout) ;
if ( immediate) {
let callNow = ! timeout;
timeout = setTimeout ( function ( ) {
timeout = null ;
} , wait)
if ( callNow) {
func . apply ( self, args) ;
}
} else {
timeout = setTimeout ( function ( ) {
func . apply ( self, args) ;
} , wait) ;
}
}
}
节流
function throttled ( func, wait ) {
let oldTime = new Date ( ) ;
return function ( ... args) {
let nowData = new Date ( ) ;
if ( nowData - oldTime >= wait) {
func . apply ( null , args) ;
oldTime = new Date ( ) ;
}
}
}
function throttled ( func, wait ) {
let timer = null ;
return function ( ... args) {
if ( ! timer) {
timer = setTimeout ( ( ) => {
func . apply ( null , args) ;
timer = null ;
} , wait) ;
}
}
}
function throttled ( func, wait ) {
let oldTime = new Date ( ) ;
let timer = null ;
return function ( ) {
let curTime = new Date ( ) ;
let remain = wait - ( curTime - oldTime) ;
let self = this ;
let args = arguments;
clearTimeout ( timer) ;
if ( remain <= 0 ) {
func . apply ( self, args) ;
oldTime = new Date ( ) ;
} else {
timer = setTimeout ( func, remain) ;
}
}
}
原生ajax
流程
创建一个ajax对象 xhr 打开地址 发送 等待数据 ajax的状态: ajax的状态有哪些
0-未初始化,尚未调用open()方法 1-启动,调用open()方法,已调用send()的方法,正在发送请求 2-发送,已经调用send()方法,已接受到响应 3-解析 正在解析响应数据 4-完成,响应数据解析完成,客户端可以调用(一般使用xhr.readyState == 4 判断ajax请求是否结束)
获取返回的结果:responseText
具体实现
function sendAjax ( obj ) {
var url = obj. url
var method = obj. method
var async = obj. async== undefined ? true : obj. async
var data = obj. data
function splitStr ( data ) {
let str = ''
for ( var key in data) {
let s = key + '=' + data. key + '&'
str += s
}
return str. substring ( 0 , str. length - 1 )
}
var xhttp
if ( window. XMLHttpRequest) xhttp = new XMLHttpRequest ( )
else xhttp = new ActiveXObject ( )
if ( obj. method == 'get' || obj. method == 'GET' ) {
if ( data == undefined ) {
xhttp. open ( 'GET' , url, async)
xhttp. send ( )
}
else {
xhttp. open ( 'GET' , url + splitStr ( data) , async)
xhttp. send ( )
}
}
else if ( method == 'post' || method == 'POST' ) {
if ( data== undefined ) throw 'method POST can not without data to send'
else {
xhttp. open ( 'POST' , url, async)
xhttp. setRuquestHeader ( 'Content-type' , 'application/x-www-form-urlencoded' )
xhttp. send ( data)
}
}
else if ( method == undefined || method== '' ) throw 'method can not be empty'
xhttp. onreadystatechange = function ( ) {
if ( xhttp. readyState== 4 ) {
obj. success ( JSON . parse ( xhttp. responseText) )
}
else if ( xhttp. readyState== 4 && ( xhttp. status!= 200 || xhttp. status!= 304 ) ) {
obj. error ( )
}
}
}
sendAjax ( {
url: 'AJAX.json' ,
method: 'get' ,
async: true ,
data: {
id: 100 ,
username: "123456"
} ,
success : function ( data ) {
console. log ( data)
} ,
error : function ( ) {
console. log ( 'error data' )
}
} )
手写promise
未处理异步操作的简单同步的promise手写实现
class Promise {
constructor ( executor ) {
this . status = 'pending' ;
this . value = undefined ;
this . reason = undefined ;
let resolve = ( value ) => {
if ( this . status === 'pending' ) {
this . status = 'fullfilled' ;
this . value = value;
}
}
let reject = ( reason ) => {
if ( this . status === 'pending' ) {
this . status = 'rejected' ;
this . reason = reason;
}
}
try {
executor ( resolve, reject) ;
} catch ( error) {
reject ( error) ;
}
}
then ( onfullfilled, onrejected ) {
if ( this . status === 'fullfilled' ) {
onfullfilled ( this . value) ;
}
if ( this . status === 'rejected' ) {
onrejected ( this . reason) ;
}
}
}
添加上异步操作的简单promise手写实现
class Promise {
constructor ( executor ) {
this . status = 'pending' ;
this . value = undefined ;
this . reason = undefined ;
this . onresolvedCallbacks = [ ] ;
this . onrejectedCallbacks = [ ] ;
let resolve = ( value ) => {
if ( this . status === 'pending' ) {
this . status = 'fullfilled' ;
this . value = value;
this . onresolvedCallbacks. forEach ( fn => fn ( ) ) ;
}
}
let reject = ( reason ) => {
if ( this . status === 'pending' ) {
this . status = 'rejected' ;
this . reason = reason;
this . onrejectedCallbacks. forEach ( fn => fn ( ) ) ;
}
}
try {
executor ( resolve, reject) ;
} catch ( error) {
reject ( error) ;
}
}
then ( onfullfilled, onrejected ) {
if ( this . status === 'fullfilled' ) {
onfullfilled ( this . value) ;
}
if ( this . status === 'rejected' ) {
onrejected ( this . reason) ;
}
if ( this . status === 'pending' ) {
this . onresolvedCallbacks. push ( ( ) => {
onfullfilled ( this . value) ;
} )
this . onrejectedCallbacks. push ( ( ) => {
onfullfilled ( this . reason) ;
} )
}
}
}
封装promise
未封装成promise时的请求
sendAjax ( {
url: 'AJAX.json' ,
method: 'get' ,
async: true ,
data: {
id: 100 ,
username: "123456"
} ,
success : function ( data ) {
console. log ( data)
} ,
error : function ( ) {
console. log ( 'error data' )
}
} )
封装成promise时的请求
const sendAjax = ( ) => {
return new Promise ( ( resolve, reject ) => {
sendAjax ( {
url: 'AJAX.json' ,
method: 'get' ,
async: true ,
data: {
id: 100 ,
username: "123456"
} ,
success: resolve ( data) ,
error: reject ( err) ,
} )
} )
}
调用格式
sendAjax ( ) . then ( ( data ) => {
console. log ( data) ;
} , ( err ) => {
console. log ( err) ;
} )