[TOC]
  • setTimeout的误区setTimeout 函数的及时性问题会被阻塞无法捕获在与DOM事件打交道的时候参考资料

setTimeout的误区

很多人对 setTimeout 函数的理解就是:延时为 n 的话,函数会在 n 毫秒之后执行。事实上并非如此,这里会存在几个问题:

setTimeout 函数的及时性问题

var d = new Date, count = 0, f, timer;timer = setInterval(f = function (){    if(new Date - d > 1000)         clearInterval(timer), console.log(count);    count++;}, 0);

可以看出 1s 中运行的次数大概在 200次 左右,有人会说那是因为 new Date 和 函数作用域的转换消耗了时间,其实不是,而是setInterval 和 setTimeout 函数运转的最短周期是 5ms 左右,这个数值在 HTML规范 中也是有提到的:

5. Let timeout be the second method argument, or zero if the argument was omitted.如果 timeout 参数没有写,默认为 07. If nesting level is greater than 5, and timeout is less than 4, then increase timeout to 4.如果嵌套的层次大于 5 ,并且 timeout 设置的数值小于 4 则直接取 4.

如果需要更加短的周期,可以使用:

  • requestAnimationFrame 它允许 JavaScript 以 60+帧/s 的速度处理动画,他的运行时间间隔比 setTimeout 是要短很多的。
  • process.nextTick 这个是 NodeJS 中的一个函数,利用他可以几乎达到上面看到的 while 循环的效率
  • ajax 或者 插入节点 的 readState 变化
  • MutationObserver
  • setImmediate会被阻塞

由于Javascript是单线程的,所以会存在被阻塞的情况:

var d = new Date;setTimeout(function(){    console.log("show me after 1s, but you konw:" + (new Date - d));}, 1000);while(1) if(new Date - d > 2000) break;

我们期望 console 在 1s 之后出结果,可事实上他却是在 2075ms 之后运行的,这就是 JavaScript 单线程给我们带来的烦恼,while循环阻塞了 setTimeout 函数的执行。

无法捕获

try…catch捕捉不到它的错误:

try{    setTimeout(function(){        throw new Error("我不希望这个错误出现!")    }, 1000);} catch(e){    console.log(e.message);}

在与DOM事件打交道的时候

因为浏览器端主要由三个线程:javascript执行,UI渲染,事件触发队列。javascript执行的线程与UI渲染的线程又是互斥的,所以如果在一个dom事件,特别是onmousexxx和onkeyxx事件中,对另外的dom元素进行操作,比如focus的时候,需要设置setTimeout将这些操作添加到事件触发的队列中,等当然的dom操作执行完成后执行。

比如这个代码:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html><head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />    <title>setTimeout</title>    <script type="text/javascript">        function get(id) {            return document.getElementById(id);        }        window.onload = function () {            //第一个例子:未使用setTimeout            var makeBtn = get('makeinput');            makeBtn.onmousedown = function (e) {                console.log(e.type);                var input = document.createElement('input');                input.setAttribute('type', 'text');                input.setAttribute('value', 'test1');                get('inpwrapper').appendChild(input);                input.onfocus = function (e) {//观察我们新生成的input什么时候获取焦点的,或者它有没有像原文作者说的那样被丢弃了                    console.info('input focus');                };                input.focus();                input.select();            }            makeBtn.onclick = function (e) {                console.log(e.type);            };            makeBtn.onmouseup = function (e) {                console.log(e.type);            };            makeBtn.onfocus = function () {//观察我们生成按钮什么时候获取焦点的                console.log('makeBtn focus');            }            //第二个例子:使用setTimeout            var makeBtn2 = get('makeinput2');            makeBtn2.onmousedown = function (e) {                console.log(e.type);                var input = document.createElement('input');                input.setAttribute('type', 'text');                input.setAttribute('value', 'test1');                get('inpwrapper2').appendChild(input);                input.onfocus = function (e) {//观察我们新生成的input什么时候获取焦点的,或者它有没有像原文作者说的那样被丢弃了                    console.info('input focus');                };                //setTimeout                setTimeout(function () {                    input.focus();                    input.select();                }, 0);            }            makeBtn2.onclick = function (e) {                console.log(e.type);            };            makeBtn2.onmouseup = function (e) {                console.log(e.type);            };            makeBtn2.onfocus = function () {//观察我们生成按钮什么时候获取焦点的                console.log('makeBtn2 focus');            }            //第三个例子,onkeypress输入的时候少了一个值            get('input').onkeypress = function () {                get('preview').innerHTML = this.value;            }        }    </script></head><body>    <h1><code>setTimeout</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>    <p id="inpwrapper2"></p>    <h2>3、另一个例子</h2>    <p>        <input type="text" id="input" value="" /><span id="preview"></span>    </p></body></html>

你能说出点击对应的输出结果吗,并告知原因?

参考资料

  • JavaScript异步编程原理
  • JavaScript秘密花园
  • javascript线程解释(setTimeout,setInterval你不知道的事)

原文: https://leohxj.gitbooks.io/front-end-database/content/problems-in-develop-webapp/problem-with-settimeout.html