写在后面:
自己没有这道题的标准答案,百度上也查不到相干的题解,以下思路都是自己集体想法,如有更好的答案,欢送提出!
网易前端校招的一道面试题,对一个事件循环内所有调用进行防抖。
大家对防抖应该都有所耳闻,但什么是对一个事件循环内的调用进行防抖?自己也从未据说过,看第一遍的时候也没有想法。
然而这道题总结下来,应该是要实现两个需要:
- 屡次同步调用
G
,只执行第一次 - 别离应用
setTimeout
异步调用G
和同步调用G
,各执行一次
既然是两个需要,那就一个一个来
先来实现第一个需要,这个比较简单,甚至也不必关怀事件循环。跟 once
函数的实现比拟相似,每次调用之前先判断一下是否曾经调用,如已调用就不再调用了。
function debounce(func) {
let called = false;
return function() {if(!called) {
called = true;
func();}
}
}
尽管感觉哪里不对,然而第一个需要曾经实现了。各位同学必定也发现了,下面的代码存在一个问题,也就是同步调用 G
之后,没法通过 setTimeout
再次调用了,因为 called
曾经变成了 true
。
接下来实现第二个需要。为了让 setTimeout
能够再次调用,咱们须要通过一种伎俩,在所有同步代码执行结束之后,将 called
再次改为 false
。
既然要在同步代码执行结束之后批改 called
,咱们能不能用 setTimeout
呢?依照这个思路,代码如下:
function debounce(func) {
let called = false;
return function() {if(!called) {
called = true;
func();
setTimeout(() => {called = false;}, 0)
}
}
}
测试了一下发现,第一个需要没有问题,然而第二个需要还是只打印了一次。这是因为调用程序的问题,在第二个需要下,setTimeout(G, 0)
先进入工作队列,而后执行到下一行 G()
的时候,防抖函数中的 setTimeout
才进入工作队列。大家都晓得队列是先进先出,那么 setTimeout(G, 0)
会先于防抖函数中的 setTimeout
执行,当 setTimeout(G, 0)
执行的时候,called
还是 true
,所以还是只打印一次。
要解决这个问题,就要找到一种机制,它的执行机会在同步代码之后,在 setTimeout
等异步工作执行之前。咱们晓得,JS 中的异步工作其实分为两种,宏工作 和微工作。
JS 在执行宏工作之前都会查看微工作队列,如果队列不为空,就先执行微工作,直到清空微工作队列,再执行宏工作
咱们晓得,setTimeout
、setInterval
、回调函数、文件 I /O 等都属于宏工作,因而,为了实现题目中的需要,咱们能够应用微工作批改 called
。常见的微工作包含 Promise
、MutationOserver
、process.nextTick
,咱们应用最常见的 Promise
来实现。
function debounce(func) {
let called = false;
return function() {if(!called) {
called = true;
func();
Promise.resolve().then(() => {called = false;})
}
}
}
通过测试,完满实现题目中的两个需要。