事件循环【Eventloop】
js是单线程非阻塞的脚本语言,单线程意味着在同一时间,只能做同一件事,所以js代码执行时只有一个主线程在执行。而非阻塞是指当代码需要进行一项异步任务(也就是不能及时获取结果,需要等待一定时间的任务)时,主线程会暂时挂起这个任务,然后在异步任务返回结果的时候根据返回的结果去执行相应的回调。
流程图
js 异步执行的运行机制
所有任务都在主线程上执行,形成一个执行栈。主线程之外,还存在一个任务队列(task queue)。只要异步任务有了运行结果,就在任务队列之中放置一个事件。
一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。主线程将会不断重复上述步骤。
名词解释
执行栈
执行任务队列中的某个任务,这个被执行的任务就称为执行栈
主线程
主线程规定现在执行执行栈中的哪个事件。
当遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中,我们称之为任务队列。
当主线程将执行栈中所有的代码执行完之后,主线程将会去查看任务队列是否有任务。如果有,那么主线程会依次执行那些任务队列中的回调函数。
主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码。
异步任务
异步任务又分为宏任务和微任务。
微任务
一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
常见的微任务有:
- Promise.then
- MutaionObserver
- Object.observe(已废弃;Proxy 对象替代)
- process.nextTick(Node.js)
宏任务
宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合
常见的宏任务有:
- script (可以理解为外层同步代码)
- setTimeout/setInterval
- UI rendering/UI事件
- postMessage、MessageChannel
- setImmediate、I/O(Node.js)
node中的事件循环
笔者在一次面试中被问到了这个问题
在进程启动时,Node便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们称为Tick。
每个Tick的过程就是查看是否有事件待处理,如果有,就取出事件及其相关的回调函数。
如果存在关联的回调函数,就执行它们。