javaScript是单线程的可是它的背后有浏览器的其余线程为其服务,其异步也得靠其余线程来监听事件的响应,并将回调函数推入到任务队列等待执行。单线程所作的就是执行栈中的同步任前端
1、JavaScript单线程
在浏览器的一个页面中,该页面的JS程序只有一个线程,故曰单线程。由于是单线程,因此程序的执行顺序就是从上到下依次执行,同一时间内只能有一段代码被执行。那为何不用多线程,这样不是更能充分利用CPU,提升效率么?java
早期的网页内容很是简单,单线程足以应付,因此在设计之初,我估计设计者就没考虑使用多线程。另外,JavaScript主要用来处理用户与页面产生的交互,以及操做DOM;若是以多线程的方式来操做DOM,一个线程要求删除该DOM,另外一个要求修改DOM样式,那么浏览器该听谁的?这大大增长了程序设计的复杂度,原本前端都很差学,不是么~~~简简单单多好。ajax
虽然JavaScript是单线程的,但是浏览器内部不是单线程的。你的一些I/O操做、定时器的计时和事件监听(click, keydown…)等都是由浏览器提供的其余线程来完成的。浏览器
若是想利用多线程处理一些耗时较长的任务,可使用HTML5提出的Web Worker。多线程
2、任务队列和事件循环
提到异步机制,不得不说到任务队列和事件循环,这里理解JS异步机制的要点。异步
javaScript单线程和异步机制函数
左边的栈存储的是同步任务,所谓同步的任务就是那些能当即执行、不耗时的任务,如变量和函数的初始化、事件的绑定等等那些不须要回调函数的操做均可归为这一类。右边的堆用来存储声明的变量、对象。下面的队列就是任务队列,一旦某个异步任务有了响应就会被推入队列中。如用户的点击事件、浏览器收到服务的响应和后面提到的setTimeout插入的事件,北京TS。每一个异步任务都和一个回调函数相关联。spa
一个js程序的单线程用来执行栈中的同步任务,当全部同步任务执行完毕后,栈被清空,而后读取任务队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务,执行完毕。线程
单线程从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,若是没有新的任务,就会等待,直到有新的任务,这就叫任务循环。由于每一个任务都由一个事件所触发,因此也叫事件循环。设计
3、异步机制
有了上面两节作铺垫,理解异步机制就容易多了。拿ajax来讲,当页面的单线程执行xhr.send()以后,对于页面来讲发送任务已经完成了。怎么发送,那是浏览器的事,和单线程无关;何时响应,这事说不许。为了及时地获得响应的内容,在单线程中注册相应的事件就好xhr.onreadystatechange = fn() {...}。注册以后,浏览器会在内部的其余线程中自动地帮咱们监听该事件。直到该事件被触发,浏览器会在任务队列中添加一个任务等待该单线程执行。
4、定时器
setTimeout的做用是在间隔必定的时间后,将回调函数插入任务队列中,等栈中的同步任务都执行完毕后,再执行。由于栈中的同步任务也会耗时,因此间隔的时间通常会大于等于指定的时间。
setTimeout(fn, 0)的意思是,将回调函数fn马上插入任务队列,等待执行,而不是当即执行。看一个例子:
setTimeout(function() {
console.log("a")
}, 0)
for(let i=0; i<10000; i++) {}
console.log("b")
// 结果:b a
setTimeout(function() {
console.log("a")
}, 0)
for(let i=0; i<10000; i++) {}
console.log("b")
// 结果:b a
打印结果代表回调函数并无马上执行,而是等待栈中的任务执行完毕后才执行的。栈中的任务执行多久,它就得等多久。
5、总结JavaScript单线程和其异步机制就如上所述。所谓的单线程并不孤单,它的背后有浏览器的其余线程为其服务,其异步也得靠其余线程来监听事件的响应,并将回调函数推入到任务队列等待执行。单线程所作的就是执行栈中的同步任务,执行完毕后,再从任务队列中取出一个事件(没有事件的话,就等待事件),而后开始执行栈中相关的同步任务,不断的这样循环。