资讯专栏INFORMATION COLUMN

异步操作(二)定时器

ctriptech / 2567人阅读

摘要:提供定时执行代码的功能,叫做定时器,主要由和这两个函数来完成。,利用这一点,可以写一个函数,取消当前所有的定时器。回调函数必须等本轮运行完,因此时间不确定。另一个应用是,用户自定义的回调函数,通常在浏览器的默认动作之前触发。

JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成。它们向任务队列添加定时任务

setTimeout()
setInterval()
clearTimeout(),clearInterval()
实例:debounce 函数
运行机制
setTimeout(f, 0)
含义
应用

1.setTimeout()
执行多少毫秒后执行,返回一个编号(顺序递增),用于取消。
第一个参数func|code是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数。后面的参数为传给回调函数的参数
setTimeout(function (a,b) {
console.log(a + b);
}, 1000, 1, 1);

This(回调函数为对象方法)为全局
var x = 1;

var obj = {
x: 2,
y: function () {

console.log(this.x);

}
};

setTimeout(obj.y, 1000) // 1
上面代码输出的是1,而不是2。因为当obj.y在1000毫秒后运行时,this所指向的已经不是obj了,而是全局环境。

为了防止出现这个问题,一种解决方法是将obj.y放入一个函数。

var x = 1;

var obj = {
x: 2,
y: function () {

console.log(this.x);

}
};

setTimeout(function () {
obj.y();
}, 1000);
// 2
上面代码中,obj.y放在一个匿名函数之中,这使得obj.y在obj的作用域执行,而不是在全局作用域内执行,所以能够显示正确的值。

另一种解决方法是,使用bind方法,将obj.y这个方法绑定在obj上面。

var x = 1;

var obj = {
x: 2,
y: function () {

console.log(this.x);

}
};

setTimeout(obj.y.bind(obj), 1000)
//

2.setInterval()
setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
下面是一个通过setInterval方法实现网页动画的例子。

var div = document.getElementById("someDiv");
var opacity = 1;
var fader = setInterval(function() {
opacity -= 0.1;
if (opacity >= 0) {

div.style.opacity = opacity;

} else {

clearInterval(fader);

}
}, 100);
上面代码每隔100毫秒,设置一次div元素的透明度,直至其完全透明为止。
setInterval的一个常见用途是实现轮询。下面是一个轮询 URL 的 Hash 值是否发生变化的例子。

var hash = window.location.hash;
var hashWatcher = setInterval(function() {
if (window.location.hash != hash) {

updatePage();

}
}, 1000);

时间
不考虑执行时间即会小于100ms ,第二次执行就会开始。如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。
为了确保两次执行之间有固定的间隔,可以不用setInterval,而是每次执行结束后,使用setTimeout指定下一次执行的具体时间。

var i = 1;
var timer = setTimeout(function f() {
// ...
timer = setTimeout(f, 2000);
}, 2000);
上面代码可以确保,下一次执行总是在本次执行结束之后的2000毫秒开始。

3.clearTimeout(),clearInterval()
利用这一点,可以写一个函数,取消当前所有的setTimeout定时器。

(function() {
// 每轮事件循环检查一次
var gid = setInterval(clearAllTimeouts, 0);

function clearAllTimeouts() {

var id = setTimeout(function() {}, 0);
while (id > 0) {
  if (id !== gid) {
    clearTimeout(id);
  }
  id--;
}

}
})();
上面代码中,先调用setTimeout,得到一个计算器编号,然后把编号比它小的计数器全部取消

4.实例:debounce 函数
debounce(防抖动)监听时间时(Keypress,一直触发函数。
有时,我们不希望回调函数被频繁调用。比如,用户填入网页输入框的内容,希望通过 Ajax 方法传回服务器,jQuery 的写法如下。

$("textarea").on("keydown", ajaxAction);

$("textarea").on("keydown", debounce(ajaxAction, 2500));

function debounce(fn, delay){
var timer = null; // 声明计时器
return function() {

var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
  fn.apply(context, args);
}, delay);

};
}
上面代码中,只要在2500毫秒之内,用户再次击键,就会取消上一次的定时器,然后再新建一个定时器。这样就保证了回调函数之间的调用间隔,至少是2500毫秒。

5.运行机制
将指定的代码移到下一轮事件循环,等这轮轮完,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就继续等待。

回调函数必须等本轮运行完,因此时间不确定。

setTimeout(someTask, 100);
veryLongTask();
上面代码的setTimeout,指定100毫秒以后运行一个任务。但是,如果后面的veryLongTask函数(同步任务)运行时间非常长,过了100毫秒还无法结束,那么被推迟运行的someTask就只有等着,等到veryLongTask运行结束,才轮到它执行

6.setTimeout(f, 0)

6.1含义
因为上一节说过,必须要等到当前脚本的同步任务,全部处理完以后,才会执行setTimeout指定的回调函数f

setTimeout(function () {
console.log(1);
}, 0);
console.log(2);
// 2
// 1
上面代码先输出2,再输出1。因为2是同步任务,在本轮事件循环执行,而1是下一轮事件循环执行。
setTimeout(f, 0)会在下一轮事件循环一开始就执行
6.2应用
6.2.1它的一大应用是,可以调整事件的发生顺序。
比如,网页开发中,某个事件先发生在子元素,然后冒泡到父元素,即子元素的事件回调函数,会早于父元素的事件回调函数触发。如果,想让父元素的事件回调函数先发生,就要用到setTimeout(f, 0)。
// HTML 代码如下
//

var input = document.getElementById("myButton");

input.onclick = function A() {
setTimeout(function B() {

input.value +=" input";

}, 0)
};

document.body.onclick = function C() {
input.value += " body"
};
上面代码在点击按钮后,先触发回调函数A,然后触发函数C。函数A中,setTimeout将函数B推迟到下一轮事件循环执行,这样就起到了,先触发父元素的回调函数C的目的了。

6.2.2另一个应用是,用户自定义的回调函数,通常在浏览器的默认动作之前触发。

比如,用户在输入框输入文本,keypress事件会在浏览器接收文本之前触发。因此,下面的回调函数是达不到目的的。

// HTML 代码如下
//

document.getElementById("input-box").onkeypress = function (event) {
this.value = this.value.toUpperCase();
}
上面代码想在用户每次输入文本后,立即将字符转为大写。但是实际上,它只能将本次输入前的字符转为大写,因为浏览器此时还没接收到新的文本,所以this.value取不到最新输入的那个字符。只有用setTimeout改写,上面的代码才能发挥作用。

document.getElementById("input-box").onkeypress = function() {
var self = this;
setTimeout(function() {

self.value = self.value.toUpperCase();

}, 0);
}
上面代码将代码放入setTimeout之中,就能使得它在浏览器接收到文本之后触发

由于setTimeout(f, 0)实际上意味着,将任务放到浏览器最早可得的空闲时段执行,所以那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)里面执行。

var div = document.getElementsByTagName("div")[0];

// 写法一
for (var i = 0xA00000; i < 0xFFFFFF; i++) {
div.style.backgroundColor = "#" + i.toString(16);
}

// 写法二
var timer;
var i=0x100000;

function func() {
timer = setTimeout(func, 0);
div.style.backgroundColor = "#" + i.toString(16);
if (i++ == 0xFFFFFF) clearTimeout(timer);
}

timer = setTimeout(func, 0);
上面代码有两种写法,都是改变一个网页元素的背景色。写法一会造成浏览器“堵塞”,因为 JavaScript 执行速度远高于 DOM,会造成大量 DOM 操作“堆积”,而写法二就不会,这就是setTimeout(f, 0)的好处。

6.2.3另一个使用这种技巧的例子是代码高亮的处理
如果代码块很大,一次性处理,可能会对性能造成很大的压力,那么将其分成一个个小块,一次处理一块,比如写成setTimeout(highlightNext, 50)的样子,性能压力就会减轻

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105806.html

相关文章

  • 总结javascript基础概念():事件队列循环

    摘要:而事件循环是主线程中执行栈里的代码执行完毕之后,才开始执行的。由此产生的异步事件执行会作为任务队列挂在当前循环的末尾执行。在下,观察者基于监听事件的完成情况在下基于多线程创建。 主要问题: 1、JS引擎是单线程,如何完成事件循环的? 2、定时器函数为什么计时不准确? 3、回调与异步,有什么联系和不同? 4、ES6的事件循环有什么变化?Node中呢? 5、异步控制有什么难点?有什么解决方...

    zhkai 评论0 收藏0
  • javascript基础总结()——异步编程情况

    摘要:异步规定要做一件事,不是立马执行这件事,需要等一定的时间,这样的话,我们不会等着它执行,而是继续执行下面的操作,只有将下面的事情处理完了,才会返回头处理之前的事情如果下面的事情并没有处理完成,不管之前的事情有没有到时间,都踏踏实实的给我等着 异步:规定要做一件事,不是立马执行这件事,需要等一定的时间,这样的话,我们不会等着它执行,而是继续执行下面的操作,只有将下面的事情处理完了,才会返...

    Dionysus_go 评论0 收藏0
  • 异步操作时器

    摘要:提供定时执行代码的功能,叫做定时器,主要由和这两个函数来完成。,利用这一点,可以写一个函数,取消当前所有的定时器。回调函数必须等本轮运行完,因此时间不确定。另一个应用是,用户自定义的回调函数,通常在浏览器的默认动作之前触发。 JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成。它们向任务队列...

    xcc3641 评论0 收藏0
  • swoole——从入门到放弃(

    摘要:从入门到放弃二一异步毫秒定时器设置一个间隔时钟定时器,与定时器不同的是定时器会持续触发,直到调用清除。是一次性函数,执行完成后就会销毁最大不超过使用定时器来删除定时器。 swoole——从入门到放弃(二) 一、异步毫秒定时器 swoole_timer_tick:设置一个间隔时钟定时器,与after定时器不同的是tick定时器会持续触发,直到调用swoole_timer_clear清...

    awokezhou 评论0 收藏0

发表评论

0条评论

ctriptech

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<