JavaScript的单线程
众所周知JavaScript程序是单线程的执行,也就是说,同一时刻只能做一件事,这也是由于JavaScript本身就是为了浏览器而生,主要用途是用户交互和操作dom,为什么是单线程?试想如果两个线程同时操作一个dom结点,那页面页面该怎样渲染,是不是会出问题呢?
同步任务/异步任务
虽然是单线程执行,但是JavaScript本身是包含同步和异步的两种任务的,也就是说,如果一个任务耗时很长,那我们可以把这个任务放到异步去执行,后面的任务就不用等待这个任务结束,就可以执行了。
- 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
- 异步任务:不进入主线程、而进入任务队列(task queue)的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
EventLoop
说完了同步任务和异步任务,那JavaScript是用怎样的机制调度执行这两种任务的呢?这里就引入了EventLoop(事件循环),我们先看下一JavaScript的引擎模型:
js-eventloop.png
下面我们来说明一下引擎模型的几个重要部分
-
stack 栈
JavaScript的函数调用形成了一个栈帧,执行前函数先入栈,执行完后函数出栈;
Undefined、Null、Boolean、Number 和 String,这5中基本数据类型可以直接访问,他们是按照值进行分配的,存放在栈(stack)内存中的简单数据段,数据大小确定,内存空间大小可以分配。 -
heap 堆
对象被分配在堆中,即用以表示一个大部分非结构化的内存区域 -
callback queue 消息队列
一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都有一个为了处理这个消息相关联的函数。
EventLoop运行机制
- 所有同步任务都在主线程上执行,形成一个执行栈,如若遇到了一些异步操作像回调函数以及ajax、setTimeout等,会先将他们交给浏览器的其他模块去执行。
- 异步操作执行完成,就到消息队列中排队,等待执行。
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取消息队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
从代码执行顺序的角度来看,程序最开始是按代码顺序执行代码的,遇到同步任务,立刻执行;遇到异步任务,则只是调用异步函数发起异步请求。此时,异步任务开始执行异步操作,执行完成后到消息队列中排队。程序按照代码顺序执行完毕后,查询消息队列中是否有等待的消息。如果有,则按照次序从消息队列中把消息放到执行栈中执行。执行完毕后,再从消息队列中获取消息,再执行,不断重复。由于主线程不断的重复获得消息、执行消息、再取消息、再执行。所以,这种机制被称为事件循环。
事件循环讲完了,写一段代码测试一下,用最简单的setTimeout函数模拟异步操作,具体地理解一下事件循环的机制:
setTimeout(function(){console.log("1")}, 1000);
setTimeout(function(){console.log("5")}, 5000);
setTimeout(function(){console.log("2")}, 2000);
setTimeout(function(){console.log("0")});
console.log("abc");
执行结果如下,你理解事件循环了吗?
abc
0
1
2
5
网友评论