MST

星途 面试题库

面试题:JavaScript 中 setTimeout 与 setInterval 的执行机制差异

请详细阐述 JavaScript 中 setTimeout 和 setInterval 在执行机制上有哪些不同点,并且举例说明在哪些场景下会因为这种差异导致不同的结果。
23.6万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

1. 执行机制不同点

  • setTimeout:设置一个定时器,在指定的延迟时间(单位:毫秒)过后,将回调函数添加到任务队列中。当调用栈为空时,任务队列中的回调函数才会被压入调用栈执行。它只执行一次回调函数。例如:
setTimeout(() => {
    console.log('这是setTimeout的回调');
}, 2000);

上述代码中,2秒(2000毫秒)后console.log('这是setTimeout的回调');会被添加到任务队列,等调用栈空闲时执行。

  • setInterval:同样设置一个定时器,按照指定的时间间隔(单位:毫秒)不断地将回调函数添加到任务队列中。只要页面不关闭或者没有调用clearInterval取消定时器,回调函数就会持续被添加到任务队列,当调用栈空闲时就执行。例如:
let count = 0;
let intervalId = setInterval(() => {
    console.log(`这是setInterval的回调,执行次数: ${++count}`);
    if (count === 5) {
        clearInterval(intervalId);
    }
}, 1000);

上述代码中,每1秒(1000毫秒)console.log(这是setInterval的回调,执行次数: ${++count});就会被添加到任务队列,等调用栈空闲时执行,直到count等于5时通过clearInterval停止定时器。

2. 场景及因差异导致的不同结果

  • 动画场景:如果要创建一个平滑过渡的动画,使用setInterval可能会导致动画卡顿。因为setInterval不管上次回调函数是否执行完毕,都会按照固定间隔时间将新的回调函数添加到任务队列。例如,在一个复杂的动画计算中,上次计算还未完成,新的计算又被添加到任务队列,可能导致动画帧丢失,画面卡顿。而setTimeout可以在每次动画帧计算完成后,再设置下一次动画帧的时间,保证动画的流畅性。
// 使用setInterval实现动画(可能卡顿)
let pos = 0;
let intervalId = setInterval(() => {
    pos += 5;
    document.getElementById('element').style.left = pos + 'px';
    if (pos >= 200) {
        clearInterval(intervalId);
    }
}, 50);

// 使用setTimeout实现动画(更流畅)
function animate() {
    pos += 5;
    document.getElementById('element').style.left = pos + 'px';
    if (pos < 200) {
        setTimeout(animate, 50);
    }
}
animate();

在上述代码中,setInterval版本可能出现计算还未完成就开始下一次计算的情况,而setTimeout版本可以确保每次动画计算完成后再开始下一次,使动画更流畅。

  • 数据轮询场景:假设要定期从服务器获取最新数据。使用setTimeout时,每次获取数据后,再设置下一次获取的时间,这样可以确保每次获取数据的操作都完成后才进行下一次请求。而setInterval可能在前一次请求还未完成时,就发起了新的请求,造成不必要的资源浪费和数据冲突。
// 使用setTimeout轮询数据
function fetchData() {
    // 模拟数据请求
    console.log('开始获取数据');
    setTimeout(() => {
        console.log('数据获取完成');
        setTimeout(fetchData, 5000);
    }, 2000);
}
fetchData();

// 使用setInterval轮询数据(可能有问题)
let intervalId = setInterval(() => {
    console.log('开始获取数据');
    setTimeout(() => {
        console.log('数据获取完成');
    }, 2000);
}, 5000);

在上述代码中,setInterval版本可能在2秒的数据请求未完成时,5秒后又发起新的请求,而setTimeout版本可以保证前一次请求完成后再发起新请求。