大公司的面试感觉一般比较注重基础,最近去本地一家大厂面试,感觉面试官可能比较看js基础,看到我简历上提到了去抖,就让我讲一下去抖是什么,怎么实现的,当时我凭着印象讲的不够清楚,面试官就继续往细节问,进一步引出了同步、异步还有事件循环的概念,其实这些理论自己以前也学过,只是没有自己整理下来,随着日常工作就慢慢忘了。这里总结一下,方便以后快速复习。
去抖的简单理解
- 应用场景:一些事件触发的操作需要延迟执行,比如在上家公司有次自己写tab切换用了
mouseenter
触发,后来有后端程序员发现有时鼠标只是无意滑过tab项,并不想触发切换,这时就可以用一个定时器延迟切换操作,但是如果短期内多次触发事件,就会有多个定时器,每个定时器的回调函数都会放到任务队列里,在延迟时间到后,可能会反复执行操作,直到任务队列里每个定时器的回调函数都执行完,结果就是可能会看到tab切换反复执行,这显然不是我们所期望的; - 因为用户有可能反复触发事件,所以每次触发事件时要判断先前的定时器是否为null,如果不是就要清除定时器;
- 在定时器的回调函数最后要把定时器设为null,如果执行了回调函数,下次触发事件就不需要清除定时器了,而是新建定时器;
- 以上能保证事件反复触发时操作不会反复执行。
异步与同步
因为去抖我讲的不够清楚,面试官又追问了细节,当时我实在想不起来,面试官又问我知不知道事件循环,这个我也看过,只是没有复习,也想不起来了,最后面试官又问我什么是同步、什么是异步,两者的区别是什么、setTimeout
是同步还是异步,这几个问题我倒是答上来了,不过由于事件循环的基本概念没讲出来,我没能展开讲,异步问题不愧为"JS三座大山",面试前一天我刚好复习的是“另外两座”原型链和闭包,现在想来是有点可惜的。
首先setTimeout
是异步的,它实现的功能是经过一段时间后把要执行的代码(通常是一个回调函数)放入一个任务队列,JS是单线程的,因此一定时间内只能执行一段代码;setTimeout
的第二个参数表示等待多长时间后把要执行的代码加入任务队列,如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行。
异步:用于一些耗时较长的操作,比如Ajax请求,发出请求后在等待响应的时间里程序不阻塞,继续执行后面的程序,等响应到达再执行Ajax请求的回调函数,再比如定时器setTimeout
和node.js里读文件的操作。
因此异步和同步的区别可以简单理解为不用等一段耗时的程序执行完就可以继续执行后面的程序;而同步则是不管前一段程序执行完需要多久都要等执行完了才能执行后面的程序。
事件循环(Event-loop)
有了上面这些概念,就能解释什么是事件循环(event-loop):JS在执行的时候有一个主执行进程,还有一个任务队列(有的也叫事件队列,英语原文统一是task queue,所以我觉得叫任务队列可能更准确一点),程序执行时,如果遇到同步代码就按照执行顺序加入执行栈中,然后从头开始执行,直到执行完毕,主执行进程才会空闲;如果遇到异步代码,比如定时器,会在定时器等待时间到了以后将定时任务加入任务队列,任务队列中的代码不一定立刻执行,而是在主执行进程空闲的时候按加入任务队列的先后顺序执行,如此反复,就形成了事件循环。
要注意的是:主执行进程里总是按同步方式顺序执行,而异步程序会在异步任务准备好的时候(比如定时器时间到了和Ajax响应到达时)被放入任务队列,待主执行进程空闲时再按顺序被推入执行栈中执行。
参考资料
- Javascript高级程序设计第三版
- 详解JavaScript中的Event Loop(事件循环)机制