浏览器端Event loop简介

了解过javascript的都知道其最大的特点就是单线程,也就是说同一时间只能干一件事情。那么为什么不能是多线程呢?原因很简单,多线程太复杂了,假设javascript有2个线程,一个去添加dom,一个去删除dom,那么浏览器就懵逼了,这到底要我选择哪个?所以为了避免不必要的麻烦,javascript一开始就选择了单线程。但是单线程也有问题,假设有个任务是要向服务器去请求一个文件,如果这个文件很大,那么就不能立即执行下一语句(要等到文件回来),这样就造成了浏览器假死的现象。所以html5提出了web worker标准,允许javascript创建子线程,但是规定很严格,子线程要受到主线程控制,并且不能操作dom,这种折中方法使得javascript更加灵活了。

到目前为止javascript可以有子线程了,这时候再遇到像之前提到的如果一个io操作很费时间,那么就可以把这个任务挂起来,等返回结果了再来执行这个任务。于是所有的任务都变成了2种,一种是同步任务(从上到下一步一步执行),另外一种就是异步任务(等有结果了再执行,即所谓的消息队列)。这2种任务进入到线程也不一样,同步的从上到下依次直接进入主线程形成执行栈,异步的等有返回结果了,比如ajax请求成功了,就把成功的回调放到子线程里面去(失败就把失败的回调放到子线程里面去)。现在浏览器开始执行主线程里面的执行栈了,等主线程里面的执行栈都执行完毕了,主线程就会到子线程里面去看之前挂起到任务哪些有回调了,如果有回调了,那就把该回调内容放到主线程里面去执行,等执行完毕了再去子线程看有没有新的回调了(这里要注意的是主线程全部执行完毕,才会去子线程去看),主线程不断的重复这个步骤,这就是所谓的Event loop,也就是javascript的运行机制。比较特殊的是setTimeout,setinterval这2个方法,它们也会被放倒子线程里面去,比如我使用setTimeout(fn,3000),有时候不一定是3s之后会执行fn这个事件,还要看主线程里面的任务是否完成。

案例1:

function f() {
  console.log("foo");
  setTimeout(g, 0);
  console.log("baz");
  h();
}

function g() {
  console.log("bar");
}

function h() {
  console.log("blix");
}

f();
输出的结果为:foo 、baz 、 blix 、bar

案例2 :

var req = new XMLHttpRequest();
req.open('GET', url);    
req.onload = function (){};    
req.onerror = function (){};    
req.send();
var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onload = function (){};    
req.onerror = function (){};   

这2个的执行结果都是一样的,都会先执行onload事件,因为javascript要等主线程空了才会去查看子线程有没有回调内容。

注意点:

异步的任务执行的顺序是不固定的,主要看返回的速度,假设a任务写在b任务之前,但是a任务比较大,耗时比较长,而b任务耗时短,那么b任务有了回调先会进入到子线程里面,这样会被主线程先轮询到,但是也有可能b任务网络不好,a任务先返回了,那么a任务的回调先被注册到子线程了,导致a先执行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值