js定时器原理的深度剖析

 

JavaScript是一门基于对象的弱类型语言,它作为浏览器脚本语言,主要用途是负责与页面的交互,以及操作DOM,它的执行环境是单线程的,默认情况JS是同步加载的,也就是 JS的加载是阻塞的,也就是说同一时间只能完成一件事,只能自上而下执行,万一上一行解析代码的时间很长,那么下面的代码就会被阻塞。对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验。

为了解决这个问题,利用多核CPU的计算能力,于是出现了同步和异步

同步操作,任务遵循队列顺序异步操作,就相当于并线了,因此异步任务不具有阻塞效应。同步任务都是在主线程中执行,形成了一个执行栈,直到主线程空闲时,才会去事件队列中查看是否有可执行的异步任务,如果有就推入主进程中。

JS是通过回调函数实现异步的

一旦用了setTimeout(),里面的回调函数就是异步代码,加入了任务队列,延时调用一个函数不马上执行,而是要等待主队列为空,并达到定的延时时间才会执行,而且只会执行一次。

举个栗子:

 setTimeout(function(){
      console.log("1")
 },500)
      console.log("2")
 setTimeout(function(){
      console.log("3")
 },300)
  setTimeout(function(){
     console.log("5")
 },0)
     console.log("4")

 

从输出结果中,我们就可以看书JS执行的顺序,2和4都为主队列的同步任务,一开始就执行,执行顺序是从上到下。

setTimeout()中的函数,无论写在JS代码中的哪个位置,设置的时间即使是0,都为异步执行,在任务队列中,从主线程执行一开始,就进行计时,一旦主线程空闲,时间短的就立即加入到主线程开始执行,时间长的依然在等待。所以根据设定的等待时间,执行顺序为5,3,1

利用setInterval()实现异步执行

定时器的执行原理与延时器相似,只是不清除定时器,他就会反复执行。

异步执行的两个必要条件就是:

  1.  主队列空闲     
  2.  到达执行时间

通过上面的例子大家应该就懂了定时器的执行规则,下面我们再详细的分析一下为什么是这样的一个规则,他是与浏览器的执行规则有关系的。

-------------------------------------回到主题,继续剖析------------------------------------ 

浏览器的执行原理是什么呢?

浏览器的多线程:

JS是单线程的,但是对于浏览器来说JS的执行只不过是在浏览器众多现成中的一条,我们称之为JS引擎线程。而浏览器的其他线程则是通过JS引擎线程在执行到某个特定的功能之后指定给浏览器的对应线程

同样类似的栗子:

setTimeout(console.log('定时器!'),0);
console.log("测试")

这个结果大家应该都知道了,先打印测试字样,后打印定时器字样。

原理:

首先JS线程读取到setTimeout定时器,这个时候就会执行浏览器的线程,然后跳过定时器继续执行,这个时候你就看到了弹出框的内容为测试,然后因为定时器的时间为0,所以一执行定时器线程就会即可将弹出框为定时器字样的任务添加到主线程(JS引擎线程)的队列之后,等待JS引擎的调用,这个时候我们看到的结果是先弹出测试,然后再弹出定时器

setTimeout我们可以再次定义为:

在指定时间内, 将任务放入事件队列,等待js引擎空闲后被执行.

-------------------------讲完原理还不够,定时器的this指向是啥?------------------------- 

    var name = 'my name is window';
    var obj = {
        name: 'my name is obj',
        fn: function () {
            // var that = this; 可以在此处定义一个this,改变this指向
            setTimeout(function () {
                console.log(this.name);   //my name is window 定时器中this默认指向window
                console.log(that.name)    //my name is obj 
            }, 1000)
            // 也可以使用箭头函数改变this指向
            // setTimeout(()=> {
            //     console.log(this.name);   //my name is obj
            // }, 1000)
        }    
    }
    obj.fn()

在setTimeout内部,this绑定采用默认绑定规则,也就是说,在非严格模式下,this会指向window;而在严格模式下,this指向undefined

学会定时器的原理后,来个小测试~~~~~ 

for (var i = 0; i < 5; i++) {
	console.log(i);
	setTimeout(function timer() {
		console.log(i);
	}, i * 1000);
}  //依次输出:0, 1, 2, 3, 4  接着输出5个5

稍微解释一下啦:javascript是单线程语言,只有主线程上的所有同步任务执行完毕,主线程才会读取任务队列上的异步任务。for循环属于同步任务,而定时器属于异步任务。所以会在for循环结束之后才开始执行定时器的代码。因此会输出5个5。

如果帮到你了,请点个赞呦~~~

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suoh's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值