Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setTimeout/setInterval/setImmediate #108

Open
yaofly2012 opened this issue Feb 16, 2020 · 0 comments
Open

setTimeout/setInterval/setImmediate #108

yaofly2012 opened this issue Feb 16, 2020 · 0 comments

Comments

@yaofly2012
Copy link
Owner

yaofly2012 commented Feb 16, 2020

一、setTimeout(function[, delay, arg1, arg2, ...]) / setInterval

setTimeout:告诉浏览器XXXms后向任务队列里插入个任务(callback);
setInterval:告诉浏览器每隔XXXms向任务队列里插入个任务(callback)。

1.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. 回调函数执行时间大于指定的时间

  1. 浏览器本身带有4ms的节流处理
    保护电池,省电
  2. 本质上回调函数具体什么时候执行要看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最大delay2^31 - 1(2,147,483,647 ms (大概24.8天))。如果超出该怎么办呢?
思路:切分为小段时间分片。
long-timeout

1.3 利用setTimeout(func, 0)或则setTimeout(func)创建异步任务

二、setImmediate

  1. 宏任务队列插入个任务。
  2. 代替setTimeout(callback, 0 [,param1, param2, ...]),因为setTimeout是依赖时间的,并且存在4ms的节流。但是在浏览器端兼容下比较差啊。

2.1 代替setTimeout(callback, 0 [,param1, param2, ...])

同样的效果,但执行过程不一样。使用setTimeout(func,0)的场景基本都是想着EventLoop里添加个异步任务,但setTimeout(func,0)的间接做到的:

  • 先告诉浏览器启个定时;
  • 当时间超过“0”ms(浏览器基本都是15ms)时,则把插入个异步任务。
  1. 更省电
  2. 总结下:

More efficient and consumes less power than the usual setTimeout(..., 0) pattern

2.2 polyfill

为啥宁愿优先采用window.postMessageMessageChannel,而最后才使用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);
  1. 测试发现即使把setTimeout放最前面,执行的回调函数还是落后了:
    image

  2. Chrome下onReadyStateChange方案并不行(空script并没有触发readystatechange事件)。注释说是IE6-8,额,好吧,没有验证条件。

2.2 vs process.nextTick()

参考

  1. Why does setTimeout() “break” for large millisecond delay values?
  2. setTimeout fired immediately if delay is far future
  3. Scheduling: setTimeout and setInterval
  4. NodeJS - setTimeout(fn,0) vs setImmediate(fn)
@yaofly2012 yaofly2012 added the TODO label Nov 9, 2020
@yaofly2012 yaofly2012 changed the title JS-Window对象-setTimeout/setInterval/setImmediate setTimeout/setInterval/setImmediate Nov 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant