关于Javascript 中 setTimeout和setInterval的总结和思考

1. JavaScript 单线程

我们通常说,javascript是单线程,指的是解释和执行js代码的引擎是单线程。
而对于浏览器来说,浏览器并不是单线程的,浏览器的线程通常包括:渲染引擎线程(负责页面的渲染), js引擎线程(解释和执行js代码),定时器处理线程(处理setTimeOut,setInterval),事件处理线程(比如点击事件,键盘事件等),ajax线程(处理ajax请求)。

2.关于setTimeout和setInterval

定时器仅仅是计划在未来某个时间执行,执行的时机并不能保证。因为在页面的生命周期中,不同时间可能有其他代码控制javascript执行。在页面下载完成后的代码运行、事件处理程序,ajax回调函数必须使用同样的线程运行。可以把事件处理程序和ajax回调函数,想象成一段 javascript待处理的消息队列。

 setTimeout(function () {
    console.log("setTimeOut")
}, 100);
//这样一段代码并不是 100ms之后执行,而是100ms 之后加入到消息队列中 
//执行代码的间隔的时间一般会大于等于指定的时间

setInterval(function () {
    console.log("setInterval")
},100);
//每隔100ms就将代码加入到消息队列

setTimeout(function () {
    setTimeout(arguments.callee,100);
}, 100);
//用这种方法 代替 重复定时器
复制代码

setInterval创建的定时器是重复定时器,确保代码每个一段时间就将代码加入到消息队列。这种重复的定时器规则有两个问题:1.某些间隔会被跳过 2.多个定时器的代码执行之间的间隔可能比预期小。
(具体原因,可以参考 javascript高级程序设计第三版中的22.3)

3.消息队列,事件循环

刚才提到,对于一些事件处理程序,ajax回调函数会被加入到 js待处理的消息队列中,同样的定时器处理程序,也会被添加到消息队列中,实际上一些异步的处理,都会被放入消息队列中。js会从消息队列里面取出待处理的程序执行。

左边是栈,右边是堆,下面是消息队列,js引擎会先执行栈中的任务,当栈中的任务清空之后,从消息队列中取出一个消息,每一个消息都与一个函数相关联,当栈中的任务再次被清空的时候,表示这个消息处理结束,然后在取下一个消息,依次循环(事件循环)。
具体参考
并发模型与事件循环
How JavaScript Timers Work
JavaScript异步机制详解

(function () {
      console.log('start');
      setTimeout(function cb() {
        console.log('setTimeout');
      });
      new Promise(function (resolve, reject) {
          if (true) {
            resolve();
          }
      }).then(function () {
            console.log("promise");          
      });
      console.log('end');
})();
复制代码

这段代码,看起来是promise.then()先加入了消息队列,然后才是setTimeout加入了消息队列,预期的输出结果应该是start,end, setTimeout、promise,但是实际上,输出结果却是start,end, promise, setTimeout,注意到了吗,promise在setTimeout之前就输出了。
原因是这样的:首先,一个浏览器环境,只能有一个事件循环,一个时间循环可以有多个任务队列。有一个事件循环,但是任务队列可以有多个。setTimeOut 属于 macrotask(宏任务) 而 promise.then 属于microtask(),整个script也属于macrotask,执行的时候,遇到setTimeOut,把它放入了macrotask ,遇到promise.then 把它放入了microtask, 执行完一个macrotask的时候,接下来 的执行顺序是,执行microtask队列的所有任务,microtask队列的所有任务,执行完之后,在执行macrotask 队列的任务,之后再检查,microtask队列的任务,并执行。依次循环。

具体参考Promise的队列与setTimeout的队列有何关联?

4.关于microtasks和macrotasks

microtasks包括: process.nextTick,promise,Object.observe,MutationObserver
macrotasks包括: setTimeout,setInterval,setImmediate,I/O,UI渲染

在vue.js中有这样一个方法,nextTick, 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。怎么能在下次DOM更新循环之后执行延迟回调呢?实际上在Vue.$nextTick的源码中,是通过promise将回调函数,放入了microtask中,在当前栈内的任务执行完之后,优先执行microtask中的任务。

具体参考从Vue.js源码看nextTick机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值