javascript中线程和定时器的理解

来源:互联网 发布:百度seo 价格 编辑:程序博客网 时间:2024/05/21 09:49

javascript定时器是一个比较喜欢欺骗别人的东西,不按套路出牌
在之前的认知当中,我们都是认为javascript的定时器是当运行到该定时器的执行代码的时候,该定时器的代码就会执行;但是你会发现;

div.onclick = function(){  setTimeout(function() {        document.getElementById('inputField').focus();   }, 0);  };

突然出现了一个间隔时间为0,很蒙圈,那还需要定时器有什么用呢;

setTimeout(function() {  while (true) {  }  }, 100);  setTimeout(function() {       alert('你好!');  }, 200);  setInterval(callbackFunction, 200);

在这里,你发现第一个定时器中的循环让代码出现要给死循环;之后应该运行alert和callbackFn,但是事实上这两个都没有如期出现;javascript定时器很让人着迷啊;

事实在于:javascript中,程序都是单线程运行的,浏览器不管在什么时候始终都只有一个线程在运行javascript程序;javascript是单线程运用;

那么浏览器又是如何处理单线程问题的呢?
在浏览器中允许多个线程异步执行,这些线程在内核的控制下相互配合有保持同步;对于浏览器中的js引擎来说,它是基于事件驱动的,从代码的角度来说,各种任务都是回掉函数,javascript引擎一直在等待着事件任务队列中任务的到来;由于javascript是单线程运行的,所有处在队列中的事件需要排队等候;一个一个被浏览器所执行;

js定时触发的线程:
注意这里的浏览器模型定时计数器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的,如果处于阻塞线程状态就计不了时,它必须依赖外部来计时并触发定时,所以队列中的定时事件也是异步事件.

  由图可知,在这t1的时间段内,继鼠标点击事件触发后,先前已设置的setTimeout定时也到达了,此刻对JavaScript引擎来说,定时触发线程产生了一个异步定时事件并放到任务队列中, 该事件被排到点击事件回调之后,等待处理.

  同理, 还是在t1时间段内,接下来某个setInterval定时器也被添加了,由于是间隔定时,在t1段内连续被触发了两次,这两个事件被排到队尾等待处理.

  可见,假如时间段t1非常长,远大于setInterval的定时间隔,那么定时触发线程就会源源不断的产生异步定时事件并放到任务队列尾而不管它们是否已被处理,但一旦t1和最先的定时事件前面的任务已处理完,这些排列中的定时事件就依次不间断的被执行,这是因为,对于JavaScript引擎来说,在处理队列中的各任务处理方式都是一样的,只是处理的次序不同而已.

  t1过后,也就是说当前处理的任务已返回,JavaScript引擎会检查任务队列,发现当前队列非空,就取出t2下面对应的任务执行,其它时间依此类推,由此看来:

javascript的单线程:如果队列非空,引擎就从队列头取出一个任务,直到该任务处理完,即返回后引擎接着运行下一个任务,在任务没返回前队列中的其它任务是没法被执行的.
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

如果你在javascript代码中插入一个setTimeout,并且设置时间为0;只是代表这个任务会被立刻插入到队列中,但不是立即执行;所以javascript中的定时器可能会出现不按预想中的那样执行的效果;
一般来说定时器是最后执行的代码;
定时器最重要的是: 指定的时间间隔表示何时将定时器的代码添加到队列中,而不是何时执行代码;
当使用setInterval时,仅当没有定时器的任何其它代码时,才将定时器代码添加到队列中,这样会确保定时器代码加入到队列的时间的间隔是为指定的间隔,请注意这只是加入队列的时间间隔,并不是执行代码的时间间隔,所以使用setInterval还是会存在两个问题:
(1)某些间隔会被跳过了
(2)多个定时器的代码执行间隔可能会比预期的要小。
注意:最好不要使用setInterval,尽量使用setTimeout的延时递归来代替setInterval
因为使用setInterval来进行回掉容易产生回掉堆积,特别是时间非常短的时候;

function Timer() {  this.s1 = 0;  this.s2 = 0;  // 箭头函数  setInterval(() => this.s1++, 1000);  // 普通函数  setInterval(function () {  this.s2++;  }, 1000);}var timer = new Timer();setTimeout(() => console.log('s1: ', timer.s1), 3000);setTimeout(() => console.log('s2: ', timer.s2), 3000);
原创粉丝点击