一文搞懂Event Loop
JS是一门单线程语言,那为何能够实现异步操作呢?
前言
JS是一门单线程语言,那为何能够实现异步操作呢?单线程和异步操作确实不能同时成为一个语言的特性。JS本身不能实现异步,但是JS的宿主环境(浏览器,Node)是多线程的,宿主环境通过某种方式,使得JS具备了异步的特性。
必备概念
堆(Heap),栈(Stack)、队列(Queue)
- 堆(Heap)
堆是一种数据结构,是利用完全二叉树维护的一组数据. - 栈(Stack)
栈在计算机科学中是限定仅在表尾进行插入或删除操作的线性表.后进先出(LIFO) - 队列(Queue)
Event Loop按任务分类
- 宏任务
- 微任务
Event Loop分类
- 浏览器事件循环
- node事件循环
浏览器事件循环
JS是单线程语言,浏览器只分配给JS一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。
任务队列
浏览器为这些异步任务单独开了一个线程,那么主线程是如何知道异步任务是否已经完成呢?这就需要依赖回调函数了,整个程序是靠事件驱动的,每个事件都有相应的回调函数。
1 | setTimeout(function(){ |
主线程
JS一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行。
- 浏览器为异步任务开启的线程序=>WebAPIs
- 任务队列=>callback queue
- 主线程
- 堆和栈
- 函数的执行就是通过进栈和出栈实现
- 栈stack清空时,说明一个任务已经执行完成,这时会从callback queue中寻找下一个任务推入栈中
浏览器执行流程
- 先执行主线程代码(js引擎),主线程中非宏任务和非微任务的都是同步代码。
- 主线程执行过程中遇到宏任务推到任务队列中,主线程同步代码执行完后,开始从队列中取出(先进先出)宏任务执行
- 碰到微任务push到栈中
1 | console.log('script start'); |