- 执行上下文 EC
理解:代码的执行环境
时机:代码正式执行之前会进入到执行环境
工作:
- 创建变量对象
- 变量;函数及函数参数;全局window,局部:抽象,但存在
- 确认this指向
- 全局:this — window;局部:this — 调用其的对象
- 创建作用域链
- 父级作用域链 + 当前变量对象
- 扩展 ECObj = {scope chain: 父级作用域链 + 当前作用域链}
- 事件循环event-loop
JavaScript引擎并不是独立运行的,它运行在宿主环境中,也就是web浏览器中;现在也进入了其他环境,如nodejs服务器领域。这些环境都有一个共同“点”(thread,也指线程),都提供了一种机制来处理程序中多个块的执行,且执行每 块时调用 JavaScript 引擎,这种机制被称为事件循环。也就是说,JavaScript事件调度总是由包含它的环境进行。
- 主线程运行时会产生执行栈,
栈中的代码调用某些api时,它们会在事件队列中添加各种事件(当满足触发条件后,如ajax请求完毕,setTimeout、promise,事件等)
- 而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调
- 如此循环
- 注意,总是要等待栈中的代码执行完毕后才会去读取事件队列中的事件
-⚠️ 线程-进程相关
JS引擎设计时选择了单线程。。。
浏览器内核
- GUI渲染进程
- js引擎线程
- 事件触发线程
- 定时器触发线程
- 异步http请求线程
同步与异步
- JS分为同步任务和异步任务
- 同步任务都在主线程上执行,形成一个执行栈
- 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
- 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
event-loop
- 主线程运行时会产生执行栈,
栈中的代码调用某些api时,它们会在事件队列中添加各种事件(当满足触发条件后,如ajax请求完毕)
- 而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调
- 如此循环
- 注意,总是要等待栈中的代码执行完毕后才会去读取事件队列中的事件
setTimeout、setInterval:由【定时器线程】控制,计时完成后就会将特定的事件推入事件队列中。
宏任务、微任务
- macrotask宏任务中的事件都是放在一个事件队列中的,而这个队列由事件触发线程维护(主代码块,setTimeout,setInterval等 )
- microtask微任务中的所有微任务都是添加到微任务队列(Job Queues)中,等待当前macrotask执行完毕后执行,而这个队列由JS引擎线程维护(Promise,process.nextTick等 )
总结下运行机制:
-
执行一个宏任务(栈中没有就从事件队列中获取)
-
执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
-
宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
-
当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
-
渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
-
nodejs的事件轮询机制
- timer定时器阶段:计时,和执行到点的定时器回调函数
- padding callbacks:某些系统操作,如TCP错误的回调函数
- idle prepare:准备工作
- ⭐️poll 轮询阶段(轮询队列):若轮询队列不为空,依次同步取出轮询队列中第一个回调函数执行,直到轮询队列为空,或达到系统最大限制
- 为空时:如果之前设置过setImmediate函数,直接进入下一个check阶段
- 如果之前没设置setImmediate函数,在当前poll等待,直到轮询队列添加回调函数,就去第一个情况执行
- 如果定时器到点了,也会去下一个阶段
- check检查阶段:执行setImmediate函数设置的回调函数
- close callback关闭阶段:执行close事件回调函数
process.nextTick(),在任何阶段优先执行
① process.nextTick(function(){xxx})
② setImmediate(function(){})
③ setTimeout(function(){},0)
网友评论