您的位置:首页 > Web前端 > JavaScript

【js】关于 setTimeout(0)所引发的……

2016-07-14 20:07 453 查看
在看vue的时候,看到了vue关于异步更新的原理,里面提到了setTimeout(0)。于是我想起来之前面试的时候被问过这个问题,并且在很久之后我随便查了查敷衍了事= =,到底是敷衍谁。于是昨天又去google了一下这个问题……果然发现了很多不得了的事(笑哭)。

之前知道,js是单线程的,所以在一段时间内只能执行一个任务。然后我们又知道settimeout和setinterver都是存在时延的,因为可能在计时器到达时间后,线程正在做其他的任务。所以要等到这个任务结束了才开始执行回调函数。我以为是因为这样,所以settimeout(0)由于时延,导致了在下一条语句之后才能执行。

。回想了下,那时候用的是百度,以后一定要告别度娘……

首先要说的一个是,现代浏览器一般要求settimeout这样的定时器,时延时间在4毫秒以上,如果小于4毫秒,就要往上加。(另外对于setInterval,H5规定最少时间为10毫秒)所以之前的理解从一定程度上……没什么问题TAT。

但是如果从一个稍微复杂的情况下去分析,那就可以发现……这个问题下的js机制相关的问题了。

以下是例子:

例子

这是比较多引用的一个例子,源码在这儿:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

 <html>

 <head>

 <title>setTimeout</title>

 <script type="text/javascript" >
(function(){
 
 function get(id){
  return document.getElementById(id);
 }
 
 window.onload = function(){
  get('makeinput').onmousedown = function(){
   var input = document.createElement('input');
   input.setAttribute('type', 'text');
   input.setAttribute('value', 'test1');
   get('inpwrapper').appendChild(input);
   input.focus();
   input.select();
  }
  get('makeinput2').onmousedown = function(){
   var input = document.createElement('input');
   input.setAttribute('type', 'text');
   input.setAttribute('value', 'test1');
   get('inpwrapper2').appendChild(input);
   setTimeout(function(){
    input.focus();
    input.select();
   }, 0);
  }
  get('input1').onkeypress = function(){
   get('preview1').innerHTML = this.value;
  }
  get('input2').onkeypress = function(){
                               setTimeout(function(){
   get('preview2').innerHTML = get('input2').value;
                              },0 );
  }
 }
})();

</script>

</head>

<body>
<h1><code>DEMO1</code></h1>
<h2>1、未使用 <code>setTimeout</code>(未选中文本框内容)</h2>
<button id="makeinput">生成 input</button>
<p id="inpwrapper"></p>
<h2>2、使用 <code>setTimeout</code>(立即选中文本框内容)</h2>
<button id="makeinput2">生成 input</button></h2>
<p id="inpwrapper2"></p>

--------------------------------------------------------------------------
<h1><code>DEMO2</code></h1>
<h2>1、未使用 <code>setTimeout</code>(只有输入第二个字符时,前一个字符才显示出来)</h2>
<input type="text" id="input1" value=""/><div id="preview1"></div>
<h2>2、使用 <code>setTimeout</code>(输入时,字符同时显示出来)</h2>

<input type="text" id="input2" value=""/><div id="preview2"></div>

</body>

</html>
看了这个例子觉得很神奇,但是看到原文得出的结论觉得匪夷所思。结论认为,在第一个例子,onmousedown的时候,由于单线程,在执行onmousedown,所以就抛弃了focus和select。这个说法显然是非常不正确的。

这时候提到js的执行机制:event loop。

浏览器最起码有三个线程,js线程,GUI线程,浏览器事件触发线程。js线程和GUI线程是互斥的,当页面需要渲染(回流,重绘等)的时候,js线程就挂起。js执行的时候GUI线程就先缓存。浏览器事件触发捕获相关事件,然后把事件加入到js事件队列尾部。

把js线程看做一个队列,里面执行的都是同步的任务。对于异步的任务,当异步的任务到了可以执行的时候(比如事件触发了,计时器的时间到了),就把这个任务取出来加到一个队列里面。当主线程中的任务执行完了,就到这个异步队列里面取任务执行。

如上所述的过程就是一个事件循环的过程,一系列event事件做完了,再取异步的队列做。做完了再来一轮。(我觉得是按时间顺序,然后先处理同步事件,再处理异步事件这样)

所以上面那个例子,没有执行select()和focus()只是因为执行之后,btn.focus又重新占据了焦点。

关于settimeout(0),我之前一直以为是像++x++这样有点无趣,有点变态地考察知识点的方式。然而其实人家还是有正经用途的。由于事件循环机制,可以利用settimeout(0)实现异步,并且可以改变一些事件实现的顺序。比如在用promise的时候,通过设置settimeout(0)把同步的调用变成异步的。详情可以参见promise指南中的例子

参考资料:

http://www.ruanyifeng.com/blog/2014/10/event-loop.html

http://www.cnblogs.com/xieex/archive/2008/07/11/1241137.html

http://www.cnblogs.com/silin6/p/4333999.html

http://www.cnblogs.com/zhaodongyu/p/3922961.html

http://javascript.ruanyifeng.com/bom/timer.html#toc3
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: