你的位置:首页 > Java教程

[Java教程]你真的知道setTimeout是如何运行的吗


大家看下如下代码,猜猜执行结果:
var start = new Date;
setTimeout(function(){
console.log('时间流逝了:'+(new Date - start)+'毫秒');
}, 200);
while (new Date - start < 1000) {}
console.log(1);
function doSoming(){
setTimeout(function(){
console.log('时间又流逝了:'+(new Date - start)+'毫秒');
},10);
}
doSoming();
while (new Date - start < 2000) {}
console.log(2);

结果是:
约1秒后输出:1,
再过约1秒后输出:2,
接着才立即输出:时间流逝了: 2002 毫秒
最后输出:时间又流逝了: 2003 毫秒
 
您猜对了没?
 
这里通过setTimeout来延迟执行的函数都被推到最后才执行了;
 
原理如下:
        在现有浏览器环境中,Javascript执行引擎是单线程的,主线程的语句和方法,会阻塞定时任务的运行,在Javascript执行引擎之外,存在一个任务队列,当在代码中调用setTimeout()方法时,注册的延时方法会挂到浏览器内核其他模块处理,当延时方法到达触发条件,即到达设置的延时时间时,该模块再将要执行的方法添加至该模块的任务队列中。这一过程与执行引擎主线程独立,执行引擎在主线程方法执行完毕,到达空闲状态时,才会从该模块的任务队列中顺序提取任务来执行,这期间的时间,可能大于注册任务时设置的延时时间;
        浏览器在空闲状态下,会不断的尝试从模块的任务队列中提取任务,这称为事件循环模型;
        
        再回头看下前面的代码,第二个setTimeout()的延迟方法的延迟时间是10毫秒,比第一个要早触发啊!为什么执行结果却在后面?因为它被之前的代码阻塞了约1000.5~1001毫秒了(视浏览器的处理速度),等他挂到处理模块,等到触发时间添加进任务队列时,第一个setTimeout()的延迟方法早就被添加到模块的任务队了,而引擎主线程是按顺序提取得,所以,你应该懂了吧?
        现在,如果把上面的while (new Date - start < 1000) {}改成while (new Date - start < 189) {}或者是while (new Date - start < 190) {},结果又是什么?我就不多说了!各刷新浏览器20遍,自己看结果吧!