进程与线程
进程:一个程序的运行时,是系统分配和调度资源的基本单元。
线程:是进程中相对独立的,可调度的执行单元。一个进程含有一至多个线程,一个线程一定属于一个进程。
JS为什么是单线程的?
为了避免DOM冲突,因为JS浏览器脚本,主要是与用户互动,并且能够操作DOM。假如JS是多线程的,如果对DOM某个节点上同时进行增删操作的话,那么会引起渲染引擎的混乱。因此JS是单线程,以后也将一直是单线程的。
执行栈
一个存储函数调用的栈结构,遵循后进先出的原则,当任务执行完之后,最后执行完的任务最先弹出栈。
任务队列 遵循先进先出的原则。
任务分为两种:同步任务和异步任务;
- 同步任务都在主线程上执行,形成执行栈;
- 主线程之外有一个任务队列,是异步任务返回结果按照先后顺序放入任务队列中等待执行;
- 一旦执行栈中的任务执行完毕,系统将读取任务队列,任务队列中的任务会按照先进先出的顺序进入执行栈中执行;
- 主线程不断重复第三步。
所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
Event Loop中任务分为微任务和宏任务:
宏任务有: script、setTimeout、setInterval ,setImmediate ,I/O ,UI rendering;
微任务有:process.nextTick ,promise ,MutationObserver,其中 process.nextTick 为 Node 独有。
执行顺序为:
- 先执行宏任务中的script,也就是同步任务;
- 遇到setTimeout、setInterval ,setImmediate时,会将setTimeout、setInterval,setImmediate推入相应任务队列中等待执行;
- 执行完同步任务之后执行微任务,首先执行微任务中的process.nextTick,然后是promise中then的回调函数;
- 执行完所有的微任务之后,然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是setTimeout和setInterval和setImmediate中的回调函数。
NodeJS中的Event Loop与浏览器中的Event Loop是两种不同的运行机制。
NodeJS中的Event Loop运行机制是:
- V8引擎解析JavaScript脚本。
- 解析后的代码,调用Node API。
- libuv库负责Node API的执行。
它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。 - V8引擎再将结果返回给用户。