重新认识定时器

从新认识一

平常,setTimeout函数吸收两个参数,第一个参数func|code是将要推延实行的函数名或许一段代码(引擎内部运用eval函数,将字符串转为代码),第二个参数delay是推延实行的毫秒数。然则,setTimeout 还可以增加更多参数。第二个今后的参数都将作为 推延实行函数的 参数 传入。


// 传入4个参数

setTimeout(function(a,b){

  // a=1,b=2
  console.log(a+b);

},1000,1,2);

// 3

从新认识二

IE 9.0及以下版本,只允许setTimeout有两个参数,不支持更多的参数。有以下解决要领。

第一种是在一个匿名函数内里,让回调函数带参数运转,再把匿名函数输入setTimeout。

setTimeout(function() {
  myFunc("one", "two", "three");
}, 1000);

上面代码中,myFunc是真正要推延实行的函数,有三个参数。假如直接放入setTimeout,低版本的IE不能带参数,所以可以放在一个匿名函数。

第二种解决要领是运用bind要领,把过剩的参数绑定在回调函数上面,天生一个新的函数输入setTimeout。

setTimeout(function(arg1){}.bind(undefined, 10), 1000);

上面代码中,bind要领第一个参数是undefined,示意将原函数的this绑定全局作用域,第二个参数是要传入原函数的参数。它运转后会返回一个新函数,该函数不带参数。

从新认识三

假如被setTimeout推延实行的回调函数是某个对象的要领,那末该要领中的this关键字将指向全局环境,而不是定义时地点的谁人对象。

举例1


var x = 1;

var o = {
  x: 2,
  y: function(){
    console.log(this.x);
  }
};

setTimeout(o.y,1000);
// 1
// 上面代码输出的是1,而不是2,这示意o.y的this所指向的已不是o,而是全局环境了。

举例2


function User(login) {
  this.login = login;
  this.sayHi = function() {
    console.log(this.login);
  }
}

var user = new User('John');

setTimeout(user.sayHi, 1000);

// undefined
// 上面代码只会显现undefined,因为比及user.sayHi实行时,它是在全局对象中实行,所以this.login取不到值。

解决办法:

要领一 将user.sayHi放在函数中实行,sayHi是在user作用域内实行,而不是在全局作用域内实行,所以可以显现准确的值

setTimeout(function() {
  user.sayHi();
}, 1000);

要领二 运用bind要领,将绑定sayHi绑定在user上面

setTimeout(user.sayHi.bind(user), 1000);

从新认识四

HTML 5规范划定,setTimeout的最短时候距离是4毫秒。为了节电,关于那些不处于当前窗口的页面,浏览器会将时候距离扩大到1000毫秒。别的,假如笔记本电脑处于电池供电状况,Chrome和IE 9以上的版本,会将时候距离切换到体系定时器,大约是15.6毫秒。setInterval的最短距离时候是10毫秒,也就是说,小于10毫秒的时候距离会被调解到10毫秒。

从新认识五

  • setInterval函数的用法与setTimeout完全一致。

  • setInterval指定的是“最先实行”之间的距离,并不斟酌每次使命实行自身所斲丧的时候。因而现实上,两次实行之间的距离会小于指定的时候。比方,setInterval指定每100ms实行一次,每次实行须要5ms,那末第一次实行完毕后95毫秒,第二次实行就会最先。假如某次实行耗时迥殊长,比方须要105毫秒,那末它完毕后,下一次实行就会马上最先

  • 为了确保两次实行之间有牢固的距离,可以不必setInterval,而是每次实行完毕后,运用setTimeout指定下一次实行的详细时候。

写个demo,确保 下一个对话框总是在封闭上一个对话框今后2000毫秒弹出:


var i=1;
var timer=setTimeout(function(){

    alert(i);
    timer =setTimeout(arguments.callee,2000);

},2000)

用setTimeout模拟了setInterval

function interval(func,wait){

    var interv=function(){
        func.call();
        setTimeout(interv,wait);
    }

    setTimeout(interv,wait);
}

interval(function(){console.log(1)},1000)

从新认识六

setTimeout和setInterval返回的整数值是一连的,也就是说,第二个setTimeout要领返回的整数值,将比第一个的整数值大1。运用这一点,可以写一个函数,作废当前一切的setTimeout。

clearTimeout现实运用的例子。有些网站会及时将用户在文本框的输入,经由过程Ajax要领传回服务器,jQuery的写法以下。


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

如许写有一个很大的瑕玷,就是假如用户一连击键,就会一连触发keydown事宜,形成大批的Ajax通讯。这是不必要的,而且极能够会发作机能题目。准确的做法应该是,设置一个门坎值,示意两次Ajax通讯的最小距离时候。假如在设定的时候内,发作新的keydown事宜,则不触发Ajax通讯,而且从新最先计时。假如过了指定时候,没有发作新的keydown事宜,将举行Ajax通讯将数据发送出去。

debounce 防抖动要领


function debounce(fn, delay){

  var timer = null; // 声明计时器

  return function(){

    //保留当前作用域的 this和 arguments
    var context = this;
    var args = arguments;

    clearTimeout(timer);
    timer = setTimeout(function(){

      fn.apply(context, args);

    }, delay);

  };
}

// 用法示例
$('textarea').on('keydown', debounce(ajaxAction, 2500))

从新认识七

  • setTimeout和setInterval的运转机制是,将指定的代码移出本次实行比及下一轮Event Loop时,再搜检是不是到了指定时候。假如到了,就实行对应的代码;假如不到,就比及再下一轮Event Loop时从新推断。

  • 这意味着,setTimeout和setInterval指定的代码,必需比及本轮Event Loop的一切同步使命都实行完,再比及本轮Event Loop的“使命行列”的一切使命实行完,才会最先实行。因为前面的使命究竟须要若干时候实行完,是不确定的,所以没有办法保证,setTimeout和setInterval指定的使命,一定会根据预定时候实行。

  • setIntervel具有积累效应,假如某个操纵迥殊耗时,超过了setInterval的时候距离,排在背面的操纵会被积累起来,然后在很短的时候内一连触发,这能够或形成机能题目(比方集合发出Ajax要求)。


setInterval(function () {
  console.log(2);
}, 1000);

(function () {
  sleeping(3000);
})();

// 2,2,2
// 2
// ...

效果就是比及第二行语句运转完成今后,马上一连输出三个2,然后最先每隔1000毫秒,输出一个2。

从新认识八

  • 比及当前剧本的同步使命和“使命行列”中已有的事宜,悉数处理完今后,才会实行setTimeout指定的使命。

  • 也就是说,setTimeout的真正作用是,在“音讯行列”的现有音讯的背面再增加一个音讯,划定在指定时候实行某段代码。setTimeout增加的事宜,会鄙人一次Event Loop实行。

  • setTimeout(f, 0)将第二个参数设为0,作用是让f在现有的使命(剧本的同步使命和“音讯行列”指定的使命)一完毕就马上实行。

  • 也就是说,setTimeout(f, 0)的作用是,尽量早地实行指定的使命。而并非会马上就实行这个使命。

  • setTimeout(f, 0)指定的使命,最早也要到下一次Event Loop才会实行


setTimeout(function() {
  console.log("Timeout");
}, 0);

function a(x) {
  console.log("a() 最先运转");
  b(x);
  console.log("a() 完毕运转");
}

function b(y) {
  console.log("b() 最先运转");
  console.log("传入的值为" + y);
  console.log("b() 完毕运转");
}

console.log("当前使命最先");
a(42);
console.log("当前使命完毕");

// 当前使命最先
// a() 最先运转
// b() 最先运转
// 传入的值为42
// b() 完毕运转
// a() 完毕运转
// 当前使命完毕
// Timeout

从新认识九

可以调解事宜的发作递次。

例子1

网页开辟中,某个事宜先发作在子元素,然后冒泡到父元素,即子元素的事宜回调函数,会早于父元素的事宜回调函数触发。假如,我们先让父元素的事宜回调函数先发作,就要用到setTimeout(f, 0)。


var input = document.getElementsByTagName('input[type=button]')[0];

input.onclick = function A() {
  setTimeout(function B() {
    input.value +=' input';
  }, 0)
};

document.body.onclick = function C() {
  input.value += ' body'
};

上面代码在点击按钮后,先触发还调函数A,然后触发函数C。在函数A中,setTimeout将函数B推延到下一轮Loop实行,如许就起到了,先触发父元素的回调函数C的目标了。

例子2

用户在输入框输入文本,keypress事宜会在浏览器吸收文本之前触发。想在用户输入文本后,马上将字符转为大写。然则现实上,它只能将上一个字符转为大写,因为浏览器此时还没吸收到文本。


document.getElementById('my-ok').onkeypress = function() {
  var self = this;
  setTimeout(function() {
    self.value = self.value.toUpperCase();
  }, 0);
}

例子3


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

// 写法一  形成浏览器“梗塞”,因为JavaScript实行速率远高于DOM,会形成大批DOM操纵“聚集”

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);

从新认识十

一般使命(task)与微使命(microtask)。它们的区分在于,“一般使命”鄙人一轮Event Loop实行,“微使命”在本轮Event Loop的一切使命完毕后实行。

一般使命:

setTimeout
setInterval
setImmediate
I/O
种种事宜(比方鼠标单击事宜)的回调函数

微使命:

process.nextTick
Promise


console.log(1);

setTimeout(function() {
  console.log(2);
}, 0);

Promise.resolve().then(function() {
  console.log(3);
}).then(function() {
  console.log(4);
});

console.log(5);

// 1
// 5
// 3
// 4
// 2

参考文章
http://javascript.ruanyifeng….

    原文作者:mydef
    原文地址: https://segmentfault.com/a/1190000009140538
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞