2 回答
TA贡献1982条经验 获得超2个赞
var sec = 3;
var t = setInterval(function() {
sec--;
$(".time").html(sec);
if (sec == 0) {
setTimeout(function() {
alert("游戏结束,你的得分是" /*+ score*/ )
}, 0);
clearInterval(t);
}
}, 1000);
分析:
这个代码改不难,但是涉及到两个知识点,题主需要了解下,
1、 修改 innerHTML 与 页面渲染的问题
题主以为 alert() 先于 innerHTML 执行了,实际上不是的,执行顺序还是从上往下的,先修改的 DOM 元素的 innerHTML ,然后执行的 alert(),只不过 DOM 元素的 innerHTML 虽然改了,但是页面的渲染并没有进行,因为 js 是单线程的,只有所有的代码执行完了之后,有空闲,才会去渲染页面,实际上题主前面页面的渲染都是在 if 语句执行完后,主线程出现了空闲才进行的。
咱们改个代码,看得更清楚些:
var sec = 3;
var score = 100;
var t = setInterval(function() {
sec--;
$(".time").html(sec);
alert("游戏结束,你的得分是" + score);
if (sec == 0) {
clearInterval(t);
}
}, 1000);
这里每回都会更新页面和alert(),代码上先修改的 html,再 alert,但是实际运行的时候,你会发现,是先 alert 再渲染的 html; 所以题主产生了错觉,innerHTML 延后执行了;
总结:DOM修改是同步的,但是页面的渲染要等主线程空闲;
2、 定时器的**工作机制**
除了主JavaScript 执行进程外,还有一个需要在进程下一次空闲时执行的代码队列;
定时器对队列的工作方式是,当特定时间过去后将代码插入。注意,给队列添加代码并不意味着对它立刻执行,而只能表示它会尽快执行;
如果在这个时间点上,队列中没有其他东西,那么这段代码就会被执行;
在答案里,setTimout(fn,0)仅仅是立马把 fn 添加到了任务队列里,并没有马上执行。
考虑两处代码:
setTimeout('alert(1)',0);
alert(2);
alert(3);
此时的主线程和任务队列:
队列里的代码等主线程空闲时才会执行,所以 alert(1),会等到alert(2),alert(3)执行完毕后再执行;
alert(1);
alert(2);
alert(3);
此时的主线程和任务队列:
所以代码按照从上往下的顺序正常执行;
猜测,主线程在执行完代码后,会立马进行渲染,然后再执行任务队列里的代码,既然是队列,代码执行就是从第一个开始到最后一个结束。
所以等代码执行完毕主线程空闲时,首先执行的是渲染,然后执行的 alert();
总结:setTimout 是在指定间隔后,将代码加入任务队列里,等主线程空闲,执行渲染,再依次执行队列里的代码;
所以最前面的代码加上 setTimeout 的作用就在于把代码移到队列里去了,等主线程空闲,页面渲染,再执行 alert();
var sec = 3;
var t = setInterval(function() {
sec--;
$(".time").html(sec);
if (sec == 0) {
setTimeout(function() {
alert("游戏结束,你的得分是" /*+ score*/ )
}, 0);
clearInterval(t);
}
}, 1000);
参考资料:
Is innerHTML asynchronous?
When does InnerHTML execute immediately?
How to detect when innerHTML is complete
JavaScript 运行机制详解:再谈Event Loop
TA贡献1829条经验 获得超7个赞
var sec = 3;
var score = 100;
var t = setInterval(function(){
sec--;
$(".time").html(sec);
if(sec == 0){
setTimeout(function(){
alert("游戏结束,你的得分是" + score);
},0)
clearInterval(t);
}
},1000)
添加回答
举报