[关闭]
@Secretmm 2017-11-08T09:24:16.000000Z 字数 2779 阅读 714

setTimeout

js专题


setTimeout的唯一ID

eg:

  1. setTimeout(function() {
  2. console.log('一秒钟之后我将被打印出来')
  3. }, 1000);

结果

可以看到会有数字7出现在打印信息之前,这个7是个啥?

每一个setTimeout在执行时,会返回一个唯一ID,上图中的数字7,就是这个唯一ID。我们在使用时,常常会使用一个变量将这个唯一ID保存起来,用以传入clearTimeout,清除定时器。

  1. var timer = setTimeout(function() {
  2. console.log('如果不清除我,我将会一秒之后出现。');
  3. }, 1000)
  4. clearTimeout(timer); // 清除之后,通过setTimeout定义的操作并不会执行[setInterval也是同理]

setTimeout中操作的执行顺序

eg:

  1. var start = new Date();
  2. var end = 0;
  3. setTimeout(function() {
  4. console.log(new Date() - start);
  5. }, 500);
  6. while (new Date() - start <= 1000) {}
  7. //1005

由于js是单线程执行的,也就是说,不管如何都是有且只有一个线程在运行js程序

浏览器下的js:
浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。

上面的代码,虽然setTimeout的延时时间是500毫秒,可是由于while循环的存在,只有当间隔时间大于1000毫秒时,才会跳出while循环,也就是说,在1000毫秒之前,while循环都在占据着JavaScript线程。也就是说,只有等待跳出while后,线程才会空闲下来,才会去执行之前定义的setTimeout。

  1. var start = new Date();
  2. var end = 0;
  3. setTimeout(function() {
  4. console.log(new Date() - start);
  5. }, 5000);
  6. while (new Date() - start <= 1000) {}
  7. //5000

总结:setTimeout只能保证在指定的时间后任务(需要执行的函数)插入任务队列中等候,但是不保证这个任务在什么时候执行。一旦执行javascript的线程空闲出来,自行从队列中取出任务然后执行它。


当延迟时间设置为0

eg:

  1. var timer = setTimeout(function() {
  2. console.log('setTimeout actions');
  3. }, 0);

本意是立刻执行调用函数,但事实上,上面的代码并不是立即执行的,这是因为setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的延迟时间设置为0,被调用的程序也没有马上启动。

在高级版本的浏览器(Chrome、ie9+等),定义的最小时间间隔是不得低于4毫秒,如果低于这个值,就会自动增加。

所以说,当我们写为 setTimeout(fn,0) 的时候,实际是实现插队操作,要求浏览器“尽可能快”的进行回调,但是实际能多快就完全取决于浏览器了。

那setTimeout(fn, 0)有什么用处呢?其实用处就在于我们可以改变任务的执行顺序!因为浏览器会在执行完当前任务队列中的任务,再执行setTimeout队列中积累的的任务。

通过设置任务在延迟到0s后执行,就能改变任务执行的先后顺序,延迟该任务发生,使之异步执行。


setTimeout中回调函数的this

setTimeout() 方法是浏览器 windows对象提供的,因此第一个参数函数中的this其实是指向window对象
eg:

  1. var a = 1;
  2. var obj = {
  3. a: 2,
  4. test: function() {
  5. setTimeout(function(){
  6. console.log(this.a);
  7. }, 0);
  8. }
  9. };
  10. obj.test(); // 1

setTimeout不只两个参数

我们都知道,setTimeout的第一个参数是要执行的回调函数,第二个参数是延迟时间(如果省略,会由浏览器自动设置。在IE,FireFox中,第一次配可能给个很大的数字,100ms上下,往后会缩小到最小时间间隔,Safari,chrome,opera则多为10ms上下。)

其实,setTimeout可以传入第三个参数、第四个参数….,它们表示神马呢?其实是用来表示第一个参数(回调函数)传入的参数。
eg:

  1. setTimeout(function(a, b){
  2. console.log(a); // 3
  3. console.log(b); // 4
  4. },0, 3, 4);

一个经典的闭包问题

eg:

  1. for (var i=1; i<=5; i++) {
  2. setTimeout( function timer() {
  3. console.log(i);
  4. }, i*1000 );
  5. }

输出啥?为什么?

如果我们直接这样写,根据setTimeout定义的操作在函数调用栈清空之后才会执行的特点,for循环里定义了5个setTimeout操作。而当这些操作开始执行时,for循环的i值,已经先一步变成了6。因此输出结果总为6

如何依次输出1、2、3、4、5?
想要让输出结果依次执行,就必须借助闭包的特性,每次循环时,将i值保存在一个闭包中,当setTimeout中定义的操作执行时,则访问对应闭包保存的i值即可。
在函数中闭包判定的准则,即执行时是否在内部定义的函数中访问了上层作用域的变量。因此我们需要包裹一层自执行函数为闭包的形成提供条件。


添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注