一、为什么JavaScript是单线程?
js的单线程是由js的诞生原因决定的。早期的js定位是在浏览器中执行的脚本语言,负责与用户进行交互、操作dom等任务。
如果是多线程会带来很大的数据同步问题,HTML5新标准没有改变js单线程的本质
二、任务队列
单线程执行任务会发生排队,不同的任务执行的速度不一样,在IO设备读取数据的时候速度很慢。所以主线程挂起处于等待的任务,运行排在后面的任务,所以任务可以分成两种:同步任务(synchronous)异步任务(asynchronous)。同步任务是指只有前一个任务执行完毕,才能执行后一个任务 异步任务:不进入主线程、而进入“任务队列”的任务,只有“任务队列”通知主线程某个异步任务可以执行了,该任务才会进入主线程
异步执行运行机制
1、所有同步任务都在主线程执行,形成一个执行栈(excution content stack)
2、主线程之外还存在一个“任务队列”(task queue),只要异步任务有了结果,一个事件就会被放入task queue中
3、一旦执行栈中所有同步任务执行完毕,系统就会读取任务队列,对应异步任务结束等待,进入执行栈执行
4、主线程不断重复上面的步骤
三、事件和回调函数
“任务队列”除了io设备事件外,还包括用户产生的事件(鼠标点击、页面滚动),只要执行过回调函数,这些事件发生时就会进入“任务队列”,等待主线程读取
“回调函数”是被主线程挂起的代码,异步函数必须制定回调函数,当主线程开始执行异步任务时,就是在执行回调函数
主线程读取是自动的,只要执行栈一清空,任务队列第一位的事件就进入主线程
四、Event Loop
主线程从“任务队列”读取事件,过程是不断循环的,这种运行机制被称为Event Loop
主线程运行时产生堆和栈,栈中代码调用各种api,他们在“任务队列”中加入各种事件(click,load,done),只要栈中代码执行完毕后,主线程读取“任务队列”,执行事件指定的回调函数
五、定时器
除了防止异步事件,“任务队列”还能放置定时事件,这叫“定时器”。定时器主要由setTimeout和setTimeinterval函数执行,内部运行机制一样,区别在于前者只会执行一次,后者会不断执行
setTimeout有两个参数,第一个是回调函数,第二个是推迟时间
六、Node.js的Event Loop
Node.js也是单线程Event Loop,运行机制不同于浏览器
运行机制
1、V8引擎解析js脚本
2、解析后的代码,调用Node api
3、libuv库负责Node api执行,它将不同任务分配给不同线程,形成loop event,以异步方式将任务返回给v8引擎
4、v8引擎将结果返回用户
此外nodejs还提供了两个与任务队列有关的方法
process.nextTick,setImmediate
process.nextTick在“执行栈”尾部,下一次loop event之前执行回调函数
setImmediate则是在当前“任务队列”尾部添加事件,总是在下一次Event Loop执行