由setTimeout引出的浏览器事件循环机制

先看一下代码:

    function aa(){
        console.log("aa");
    }   

    function bb(){
        console.log("bb");
    }
    function cc(){
        console.log("cc");
    }
    setTimeout(cc,100);
    setTimeout(bb,500);
    setTimeout(aa,1000);

    function loop(){
    for (var i = 0; i < 1000; i++) {
        console.log("asdfghjk");
    }
}
loop();

知道setTimeout执行机制的都知道,会输出1000个asdfghjk字符串,再是cc,bb,aa。
原因是当有setTimeout存在时,首先会执行完外部的任务,再执行setTimeout。setTimeout执行顺序是先进先出。但如果第二个参数不一样,会优先执行时短的。
比如:

setTimeout(function(){
    console.log("aa");
},100);
setTimeout(function(){
    console.log("bb");
},100);
setTimeout(function(){
    console.log("cc");
},100);
setTimeout(function(){
    console.log("dd");
},50);

结果为dd aa bb cc.
然而为什么setTimeout要被放到所有的任务执行完之后执行?所以这就引出了js的运行机制。

首先js是单线程的,意味着一次只能执行一个任务。为什么会是单线程的?看过最经典的一句话就是:js主要是对DOM节点进行操作,如果js是多线程的,一个在节点上添加内容,一个要对这个dom节点进行删除,到底是以哪个为准?所以这就是js从一出现就秉承着单线程的运行机制。

所以js执行任务就是当前一个任务执行完成之后,再执行下一个任务。
那么当js执行任务时,有的任务耗时短,有的任务耗时长,那么后边的任务就要一直等到前面的任务执行完成之后才可以执行。
如果耗时长的是由于计算量大,cpu一直忙着计算的话,就还好,可大部分事件就浪费在了IO上(ajax从网络上获取数据),还有其他的如鼠标的点击,setTimeout等等。

所以就出现了将耗时长的任务先挂起,让其他的任务先执行,执行完成之后执行那些已经具备执行条件的任务。

所以所有的任务分为同步和异步。js中将同步任务放到主线程上执行,形成“执行栈”。异步任务放到任务队列中,任务队列中的任务达到执行要求后会通知主线程。当所有主线程上的任务执行完成后再去将任务队列中已经可以执行的任务放到主线程去执行。

比如setTimeout。如果设置第二个参数为10000ms,那么这个任务就会在10s后执行。其他的任务就要等10秒,这会导致运行效率极大地下降。而js的这种机制可以很好的提高效率。先将其他的同步任务加入到主线程中。将异步任务加入到任务队列中。同步任务执行时,异步任务处于挂起的状态,所以可以说也在执行只是不返回结果。当同步任务执行完成之后去执行setTimeout,这时候或许同步任务的时间大于10s,所以setTimeout可以立即执行。如果小于10s,那么setTimeout也不会等待10s后再执行,因为同步任务的执行肯定是耗费一些时间的。

说了这么多,看一下下面的例子,看看会输出什么。

console.log("1");

setTimeout(function(){
    console.log("3");
},1000);

for(var i=0;i<1000;i++){  //循环C
     console.log("a");
}
console.log("2");

document.getElementById('button').onclick=function(){     
   setTimeout(function(){
    console.log("4")
   },5000);
console.log("5");
}

上面这个例子,
① 当在1s后点击按钮时 会打印出1,1000个a,2,3,5,4。
② 当在刚开始就点击按钮时: 1,1000个a,2、5、3、4。
③ 不点击按钮就只会打印出1,1000个a,2,3。

首先这个代码中,会将setTimeout(function(){console.log(“3”);},1000);和那个click事件加入到任务队列中挂起。等待主线程上的任务执行完成之后再去将符合条件的异步任务加入到主线程中执行。
①。当1s之后点击按钮时,3已经打印出来了。而click函数内部的setTimeout将被挂起,先执行同步任务,所以当点击按钮,算上执行console.log(5)的时间 5s之后会打印出4。
②. 当刚开始就点击按钮,首先1,1000个a,2毫无疑问是先打印出来的。其次以及触发了click事件,所以5也是直接打印出来的,算上前面这些时间,如果加起来等于或者大于1s就会立即输出3,否则等待的时间也是小于1s的会输出。接下来输出4,它的时间是从点击按钮到出现的时间。
③。不点击按钮时,不会出现5和4。

说了这么多,最后看一下js的事件循环机制。
上面已经说到了,js会将同步函数加入到主线程中形成执行栈,异步任务加入到任务队列中最后调用。
所以当主线程执行完执行栈中的任务时,会向任务队列中读取事件,有可以执行的就放入主线程中执行,再依次读取,执行。这个机制就叫做事件循环机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值