Open
Description
一、setTimeout(function[, delay, arg1, arg2, ...])
/ setInterval
setTimeout
:告诉浏览器XXXms
后向任务队列里插入个任务(callback
);
setInterval
:告诉浏览器每隔XXXms
向任务队列里插入个任务(callback
)。
1.1 语法
- 参数
delay
作为有符号的Int-32处理的
- 对于非int32的实参会进行转int32
- 对于非法的实参当做0处理
setTimeout(function(){
console.log('hehe');
}, 3034101000)
会立马执行回调函数...
原因:上面的代码中指定的参数已经大于有符号的int-32最大值了,产生了溢出:
// 看看转成int-32的结果
3034101000>>0 // -1260866296,结果是个负数,setTimeout就当作0处理了
1.2 常见问题
1. 切到后天的tab,延时会进行节流处理(>1000ms)
浏览器的优化手段。
2. 回调函数执行时间大于指定的时间
- 浏览器本身带有4ms的节流处理
保护电池,省电 - 本质上回调函数具体什么时候执行要看
call stack
运行情况。
如果任务都比较耗时,很容造成任务队列的任务积压。
setTimeout(() => {
console.log(`1, currentTime: ${Date.now()}`)
}, 100)
setTimeout(() => {
console.log(`2, currentTime: ${Date.now()}`)
}, 200)
setTimeout(() => {
console.log(`3, currentTime: ${Date.now()}`)
}, 300)
// 耗时操作,导致任务队列的任务积压
var last = Date.now();
while(Date.now() - last < 500) {}
3. 大于int-32的delay都会立马触发回调么?
浏览器端:
setTimeout(function(){
console.log('hehe');
}, 30341010023)
30341010023也大于int-32最大值,但却没有立马执行。看来并不是大于int-32的delay都会立马触发回调。
setTimeout
函数会先把delay参数先转成int-32。
30341010023转成int-32的值为:
console.log(30341010023>>>0); // 276238951
Nodejs环境却是:
timers:
if (!(after >= 1 && after <= TIMEOUT_MAX)) {
after = 1; // schedule on next tick, follows browser behaviour
}
4. 超长时间延时
setTimeout/setInterval
最大delay
是2^31 - 1
(2,147,483,647 ms (大概24.8天))。如果超出该怎么办呢?
思路:切分为小段时间分片。
long-timeout库
1.3 利用setTimeout(func, 0)
或则setTimeout(func)
创建异步任务
二、setImmediate
- 向宏任务队列插入个任务。
- 代替
setTimeout(callback, 0 [,param1, param2, ...])
,因为setTimeout
是依赖时间的,并且存在4ms
的节流。但是在浏览器端兼容下比较差啊。
2.1 代替setTimeout(callback, 0 [,param1, param2, ...])
同样的效果,但执行过程不一样。使用setTimeout(func,0)
的场景基本都是想着EventLoop里添加个异步任务,但setTimeout(func,0)
的间接做到的:
- 先告诉浏览器启个定时;
- 当时间超过“0”ms(浏览器基本都是15ms)时,则把插入个异步任务。
- 更省电
- 总结下:
More efficient and consumes less power than the usual setTimeout(..., 0) pattern
2.2 polyfill
为啥宁愿优先采用window.postMessage
,MessageChannel
,而最后才使用setTimeout(func, 0)
?
var now = performance.now();
// setTimeout
setTimeout(() => {
console.log(`setTimeout, span=${performance.now()- now}`)
})
// MessageChannel
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = () => {
console.log(`MessageChannel, span=${performance.now() - now}`)
}
messageChannel.port2.postMessage('hello');
// postMessage
window.onmessage = () => {
console.log(`PostMessage, span=${performance.now() - now}`)
}
window.postMessage('hello');
// onReadyStateChange
var html = document.documentElement;
var script = document.createElement("script");
script.onreadystatechange = function () {
console.log(`onerror, span=${performance.now() - now}`)
script.onreadystatechange = null;
html.removeChild(script);
script = null;
}
html.appendChild(script);
-
Chrome下
onReadyStateChange
方案并不行(空script
并没有触发readystatechange
事件)。注释说是IE6-8,额,好吧,没有验证条件。