今年在面试的时候,被问到了一道关于 setTimeout() 方法的题目,题目大意是这样的。

    1. // 当 setTimeout 的延迟为0的时候,是先执行哪一个函数
    2. setTimeout(() => {
    3. alert('这里是第一个函数')
    4. }, 0)
    5. alert('这里是第二个函数');

    这个问题大致可以看成是这样,也就是当 setTimeout() 中的延迟为 0 的时候,里面的函数会先执行还是外面的函数会先执行。

    其实这样输入一个为 0 的延时在代码当中显得比较奇怪,因为 0 会使得这个函数并没有意义,但是这里设置 0 毫秒会使得理论上延迟为0,那应该是按照顺序先进行第一个alert,再发生第二个alert

    但在实操之后,可以发现是先进行了第二个alert,再进行第一个alert,仔细思考之后不难想出原因。根据现有的 JavaScript 引擎是单线程处理任务的,它会把任务放到队列中,并不会同步去执行,必须在完成一个任务后才开始另外一个任务。所以所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。这和操作系统中经典的 消费者-生产者问题 是一样的。但浏览器是多线程工作的,一个浏览器至少实现三个常驻线程:JavaScript 引擎线程,GUI渲染线程,浏览器事件触发线程。

    对于一般的 setTimeout(fn, millisec) 而言,这个方法实际上是将要进行的函数放入到任务队列中,并在时间到达 millisec 之后 再把这个任务放到浏览器的 JavaScript 引擎线程中去。同时,HTML5 标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。所以实际上这里的 setTimeout( fn, 0 ) 是与setTimeout( fn, 4 ) 一样的(另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。)。值得一提的是,实际上 fn 只是被放到了任务队列中去,必须等到当前代码执行完,主线程才会去执行这个任务,所以实际上可能延迟不止当前设置的时间,这个时间有可能只是最少延迟时间。