之因此写这篇文章是由于上周工做中使用setInterval轮询请求接口时遇到了一些问题,若是哪里理解的不对请你们多多指教~javascript
let timer1 = setTimeout(() => {
}, 1000)
console.log(timer1) // 1
let timer2 = setInterval(() => {
},1000)
console.log(timer2) // 2
复制代码
clearTimeout([定时器的排队序号])java
clearInterval([定时器的排队序号])ajax
let timer = setTimeout(() => {
// 定时器即便清除了,其返回值也不会清除,以后设置定时器的返回值也会在其返回值的基础上继续向后排,
// 相似于银行的排队领号,即便1号的业务办理完了,后面的人还是从2号开始继续领号,而不是从1开始。
clearTimeout(timer)
}, 1000)
复制代码
注意: 定时器须要手动清除,而且clearTimeout和clearInterval均可以清除setTimeout或setInterval,但并不建议这样作,容易形成混淆。
promise
let obj = {
fn() {
console.log(this) // obj
// 示例1
let timer1 = setTimeout(function() {
console.log('我是timer1的this指向:', this) // Window
}, 1000)
// 示例2 (让定时器函数中的this是obj:使用变量保存的方式)
let _this = this
let timer2 = setTimeout(function() {
console.log('我是timer2的this指向:', _this) // obj
}, 1000)
// 示例3 (让定时器函数中的this是obj:使用bind方法改变this指针)
let timer3 = setTimeout(
function() {
console.log('我是timer3的this指向:', this) // obj
}.bind(this),
1000
)
// 示例4 (让定时器函数中的this是obj:使用箭头函数,箭头函数中的this继承宿主环境(上级做用域中的this))
let timer4 = setTimeout(() => {
console.log('我是timer4的this指向:', this) // obj
}, 1000)
}
}
obj.fn()
复制代码
涉及到的知识点:JS事件循环机制EVENTLOOP浏览器
注意:异步任务之间并不相同,他们的执行优先级有区别。不一样的异步任务会被分为两类:微任务(micro task)和宏任务(macro task)
服务器
使用定时器的时候,千万不要太相信预期,延迟的时间严格来讲老是大于xxx毫秒的,至于大多少就要看当时执行的状况了。即便设置为0也不会立刻执行,HTM5规范定最小延迟时间不能小于4ms,不一样浏览器的实现不同,好比,Chrome能够设置1ms,IE11/Edge是4ms。网络
setTimeout注册的函数fn会交给浏览器的定时器模块来管理,延迟时间到了就将fn加入主进程执行队列,若是队列前面还有没有执行完的代码,则又须要花一点时间等待才能执行到fn,因此实际的延迟时间会比设置的长。如在fn以前正好有一个超级大循环,那延迟时间就不是一丁点了。异步
(function testSetTimeout() {
console.time('timer');
const timer = setTimeout(() => {
console.timeEnd('timer');
}, 10);
for(let i = 0; i < 100000000; i++) {}
})();
// timer: 59.364990234375ms 远远不止10ms
复制代码
setInterval有个讨厌的习惯,即对本身调用的代码是否报错这件事不闻不问,若是setInterval执行的代码因为某种缘由出了错,它还会持续不断(无论不顾)地调用该代码。函数
function a() {
try {
cnosole.log('单词拼写错误,应该是console')
} catch (e) {
console.log('错误了')
}
}
setInterval(a, 1000)
// 8VM69:5 错误了 (控制台每间隔一秒就会输出一个错误了)
复制代码
假设你每隔一段时间就经过Ajax轮询一次服务器,看看有没有新数据。而因为某些缘由(服务器过载、临时断网、流量剧增、用户带宽受限,等等,你的请求要花的时间远比你想象的要长。但setInterval不在意。它仍然会按定时持续不断地触发请求,最终你的客户端网络队列会塞满Ajax调用。ui
例子:下面代码并非上一次fn执行完了以后再过100ms才开始执行下一次fn。 事实上,setInterval并无论上一次fn的执行结果,而是每隔100ms就将fn放入异步队列,而两次fn之间具体间隔多久就不必定了,跟setTimeout实际延迟时间相似,和JS执行状况有关。
(function testSetInterval() {
let i = 0;
const start = Date.now();
const timer = setInterval(() => {
i++;
i === 2 && clearInterval(timer);
console.log(`第${i}次开始`, Date.now() - start);
for(let i = 0; i < 900000000; i++) {}
console.log(`第${i}次结束`, Date.now() - start);
}, 100);
})();
VM232:7 第1次开始 104
VM232:9 第1次结束 603
VM232:7 第2次开始 605
VM232:9 第2次结束 1106
复制代码
虽然每次fn执行时间都很长,但下一次并非等上一次执行完了再过100ms才开始执行的,实际上早就已经等在队列里了。 在fn被阻塞的时候,setInterval仍然在组织未来对回调函数的调用。 所以,当第一次fn函数调用结束时,已经有6次函数调用在等待执行。
最简单也是最容易控制的方案,是在回调函数内部使用setTimeout函数。
function foo(){
setTimeout(foo, 100);
}
foo();
复制代码