起因
故事起因于一段简短的代码,我以为我懂了,结果查了一天,框框中的代码为什么永远不会执行,自己做了测试,发现如果setTimeout里的code,如果是一个函数的话即使延迟时间是0,也会在函数后面执行,但是如果是一个语句,如console.info('aa');则会立即执行(主意哦我说的是一个语句,不是字符串)。
函数说明
根据高程和权威指南上面的说法,setTimeout(code,delay),delay是数字,单位是毫秒,code可以是函数或者是字符串,但是不推荐字符串,会影响性能;(权威指南)如果以0毫秒的超时时间来调用setTimeout(),那么指定的函数不会立即执行,相反会把它放到队列中去,等到前面处于等待状态的事件处理程序全部执行完成后,再“立即”调用它。(setTimeout和setInterval经常有不守时的时候)。
如果code为字符串,相当于执行eval_r(),可以看做是function(){eval_r(字符串)};表现与code为函数时是一致的,只有在执行队列为空的时候才执行(已达到延迟时间);
js中的异步原理
要先说明,js引擎是单线程滴,但是浏览器是多线程滴。基于这一点才能实现的异步,浏览器中只有一个线程用于js引擎处理事件,像setTimeout()中要计算延迟时间,浏览器的事件触发,http请求等等这些是浏览器的其他线程做的,不是js的那个线程,当这些线程完成之后会给js引擎一个通知,这些任务(回调函数)在js的任务队列中进行排队,一个一个被引擎处理。
再说setTimeout和setInterval经常不守时
setTimeout的延时为0时并不会立即触发,一个是浏览器自身就有一个几毫秒更新频率,另一个就是异步队列的问题,setTimeout中的回调函数会加入等待队列,在等待延迟触发的过程中,有新的同步脚本需要执行的话,新的同步脚本不会排在setTimeout的回调函数之后,而是立即执行。
setInterval不会考虑当前在执行什么,而是把所有的阻塞函数都排到队列尾部,当一个很长的代码块在执行时,可能把所有的setInterval回调函数都排在队列的后面,代码执行完成之后便是一大串的setInterval回调函数等待执行,并且这些函数之间没有间隔,直到全部完成。更加详细的解释可以参看
JavaScript的单线程性质以及定时器的工作原理
文章组织凌乱,加上js功力尚浅,大家也可以直接跳过参看这几篇文章,都是我绝得看的比较明白的几篇