时间延迟函数详解

js是单线程语言,但它容许经过设置超时值和间歇时间值来调度代码在特定的时刻执行。setTimeout是在指定delay时间后,将指定方法做为异步任务添加到异步任务队列中,而setInterval则是能够循环地每隔一个delay就向异步任务队列中添加一个任务javascript

语法

setTimeout

// 返回惟一的ID
let timeoutID = window.setTimeout(func[, delay, param1, param2, ...]);
let timeoutID = window.setTimeout(code[, delay]);

setInterval

// 返回惟一的ID
let intervalID = window.setInterval(func, delay[, param1, param2, ...]);
let intervalID = window.setInterval(code, delay);

clearTimeout和clearInterval

window.clearTimeout(timeoutID)
window.clearInterval(intervalID)
  • timeoutID/intervalID 能够用来做为clearTimeout/clearInterval方法的参数html

  • func是在delay毫秒以后执行的函数java

  • code 在第二种语法,是指你想要在delay毫秒以后执行的代码字符串 (使用该语法是不推荐的, 不推荐的缘由和eval()同样)web

  • delay 是延迟的毫秒数 (一秒等于1000毫秒),函数的调用会在该延迟以后发生。若是省略该参数,delay取默认值0。实际的延迟时间可能会比 delay 值长,缘由看下面的介绍。此外,根据w3c文档得知,当delay小于0的时候,delay按0来计算chrome

setTimeout 和setInterval的使用方法相似,重点以setTimeout为例,有差别的地方再单独分析。浏览器

例子

一、比较一下下面两个函数的区别框架

setTimeout(function () {
  func1()
}, 0)

setTimeout(function () {
  func1()
})

0秒延迟,此回调将会放到一个能当即执行的时段进行触发。javascript代码大致上是自顶向下的,但中间穿插着有关DOM渲染,事件回应等异步代码,他们将组成一个队列,零秒延迟将会实现插队操做。不写第二个参数,浏览器自动配置时间,在IE,FireFox中,第一次配可能给个很大的数字,100ms上下,日后会缩小到最小时间间隔,Safari,chrome,opera则多为10ms上下。
---出自《javascript框架设计》异步

若是对0s的例子不明白,没关系,咱们继续看下面这个:ide

var arr = [1,2,3];
for(var i in arr){
  setTimeout(function(){console.log(arr[i])},0);
  console.log(arr[i]);
}

控制台会打印什么?函数

1
 2
 3
 3  //3次

虽然setTimeout函数在每次循环的开始就调用了,可是却被放到循环结束才执行,循环结束,i=3,接着连续打印了3次3。
这里涉及到了js单线程执行的问题:js在浏览器中是单线程执行的,必须在完成当前任务后才执行队列中的下一个任务。此外对于js还维护着一个setTimeout队列,未执行的setTimeout任务就按前后顺序排在队列中,等到主线程的普通任务队列执行完后,主线程就按顺序执行积累在setTimeout中的任务。因此上面的问题会先打印一、二、3.最后才打印3个3。

此时就有个疑问了,设置delay为0,而实际是大于0才执行的,何须呢?
实际上这里咱们主要用来改变任务的执行顺序,由于浏览器会在执行完当前任务队列中的任务,再执行setTimeout队列中积累的任务。经过设置任务在延迟到0s后执行,就能改变任务执行的前后顺序,延迟该任务发生,使之异步执行。

二、delay后能够传递第三个及多个参数。

for (var i = 1; i < 4; i++) {
  var t = setTimeout(function(i) {
    console.log(i); // 2\. 1  4.2
    console.log(t); // 3\. 3  5.3
    clearTimeout(t);
  }, 10, i);
}
console.log(i);

控制台会打印什么?

4
 1
 3
 2
 3

此处在每次循环中,都将i做为回调函数的参数i传入,第一次传入1,第二次传入2,第三次传入3。第四次不知足循环条件,跳出循环,而后执行console.log(i)。主线程执行完当前任务队里的任务,而后开始执行seTimeout的队列。此时t为setTimeout最后的返回ID值3,则一次打印1,3 2,3。由于clearTimeout清楚了id为3的延时任务,因此只执行了两次。故一次打印41323
仅IE不支持第三个及以上参数

三、关于this指向的问题

myArray = ["zero", "one", "two"];
myArray.myMethod = function (sProperty) {
    alert(arguments.length > 0 ? this[sProperty] : this);
};

myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"
setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1,5 seconds

由setTimeout()调用的代码运行在与所在函数彻底分离的执行环境上. 这会致使,这些代码中包含的 this 关键字会指向 window (或全局)对象,这和所指望的this的值是不同的

如何解决this指向不肯定的问题,咱们能够用Function.prototype.bind()方法。

myArray = ["zero", "one", "two"];
myBoundMethod = (function (sProperty) {
    console.log(arguments.length > 0 ? this[sProperty] : this);
}).bind(myArray);

myBoundMethod(); // prints "zero,one,two" because 'this' is bound to myArray in the function
myBoundMethod(1); // prints "one"
setTimeout(myBoundMethod, 1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1500, "1"); // prints "one" after 1.5 seconds

应用

一、替换setInterval来实现重复定时
二、防止事件疯狂触发
三、IE下从新播放单次gif动画
四、blur事件延时生效

参考资料

[https://html.spec.whatwg.org/...
[http://imweb.io/topic/56ac67f...
[https://developer.mozilla.org...
[http://www.cnblogs.com/suspid...
[https://johnresig.com/blog/ho...
[http://www.cnblogs.com/snandy...
[http://www.ruanyifeng.com/blo...
[http://www.cnblogs.com/silin6...

文章地址:[http://17biu.cn/2017/07/18/se...

相关文章
相关标签/搜索