博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于Javascript 中 setTimeout和setInterval的总结和思考
阅读量:7208 次
发布时间:2019-06-29

本文共 2238 字,大约阅读时间需要 7 分钟。

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引擎会先执行栈中的任务,当栈中的任务清空之后,从消息队列中取出一个消息,每一个消息都与一个函数相关联,当栈中的任务再次被清空的时候,表示这个消息处理结束,然后在取下一个消息,依次循环(事件循环)。
具体参考

(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队列的任务,并执行。依次循环。

具体参考

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中的任务。

具体参考

转载地址:http://gkwym.baihongyu.com/

你可能感兴趣的文章
析构函数
查看>>
手写体识别中用到的Tensorflow函数复习
查看>>
php中str_repeat函数
查看>>
MSSQL sql server 2005/2008 row_number()函数应用之–删除表中重复记录,只保留一条不重复数据...
查看>>
深入浅出理解linux inode结构【转】
查看>>
MySQL 误操作后数据恢复(update,delete忘加where条件)【转】
查看>>
jquery为动态添加元素绑定点击事件
查看>>
Linux常用基本命令:三剑客命令之-awk动作用法(1)
查看>>
[设备]Linux设备是否可以被多个进程或者线程同时Open?
查看>>
Mac下的SecureCRT使用技巧
查看>>
Vue.js 2.x笔记:基本语法(2)
查看>>
转:不同域名注册商修改 DNS 方法
查看>>
JavaEE进阶——FastDFS实现分布式文件系统
查看>>
ElementUI的提示框的使用记录
查看>>
Linux c获取任意路径的硬盘使用情况
查看>>
ora-24550 signo=6 signo=11解决
查看>>
C# Bitmap长宽参数构造的图片对象的每个像素ARGB都是0
查看>>
android timed gpio (linux 3.0.0) 受时钟控制的gpio【转】
查看>>
idea 关闭代码自动折叠,形参提示,行数栏图标,启动不默认打开上次的项目...
查看>>
mybatis 获取insert返回的主键
查看>>