js执行机制

js执行机制

Heap(堆)、Stack(栈)、Queue(队列)、Event Loop(事件循环)

Heap(堆)

堆是线性数据结构,相当于一维数组,有唯一后继。

动态分配的内存,大小不定也不会自动释放,存放引用类型,指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针

堆的作用:存储引用类型值的数据

堆内存的释放 让所有引用堆内存空间地址的变量赋值为 null 即可,当堆内存没有被任何的变量或者其他东西引用时,就会在浏览器执行垃圾回收的时候,被销毁掉。

栈(Stack)

栈在程序中的设定是限定仅在表尾进行插入或删除操作的线性表。栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶

js中的栈准确来将应该叫调用栈,会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间。

栈的作用:存储基本类型值,还有一个很要的作用。提供代码执行的环境(提供一个供 JS 代码自上而下执行的环境(作用域,代码都是在栈内存中执行的)

栈内存的释放 全局作用域会在页面关闭或者刷新的时候释放。(栈内存释放后,存储在栈内存中的值也都会销毁。)

队列(Queue

和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为空队列

队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出

js中的队列

所有的同步任务都是在主进程执行的形成一个执行栈,主线程之外,还存在一个任务队列,任务队列里存放的任务分为两种任务类型,宏任务(macroTask)和微任务(microTask)。异步任务执行队列中先执行宏任务,然后清空当次宏任务中的所有微任务,然后进行下一个tick如此形成循环。

Event Loop(事件循环)

JS是一门单线程的非阻塞脚本语言,Event Loop就是为了解决JS异步编程的一种解决方案。

所谓单线程,就是指一次只能完成一件任务,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务。但如果有一个任务的执行时间很长,比如文件的读取或者数据的请求等等,那么后面的任务就要一直等待,这就会影响用户的使用体验。
为了解决这种情况,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

同步任务

代码从上到下按顺序执行

同步模式就是前一个任务执行完成后,再执行下一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的;

异步任务

异步任务分为宏任务、微任务。每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行队列上的后一个任务,而是执行回调函数;后一个任务则是不等前一个任务的回调函数的执行而执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

Macrotack(宏任务):

script(整体代码)、setTimeout、setInterval、setImmediateI/O、UI交互事件、postMessage、MessageChannel

Microtack(微任务):

Promise.then、MutationObserver、process.nextTick(Node环境)

Event Loop

JS 在解析一段代码时,会将同步代码按顺序排在某个地方,即执行栈,然后依次执行里面的函数。当遇到异步任务时就交给其他线程(执行队列)处理,待当前执行栈所有同步代码执行完成后,会从一个队列中去取出已完成的异步任务(宏任务和微任务,在异步中微任务是优于宏任务执行的)的回调加入执行栈继续执行,遇到异步任务时又交给其他线程,…..,如此循环往复。而其他异步任务完成后,将回调放入任务队列中待执行栈来取出执行。

· 如此循环,就形成js的事件循环机制(Event Loop)

JS为什么是单线程语言,那它是怎么实现异步编程(非阻塞)运行的

1、JavaScript的诞生就是为了处理浏览器网页的交互(DOM操作的处理、UI动画等), 设计成单线程的原因就是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果(两个线程修改了同一个DOM节点就会产生不必要的麻烦),这对于一种网页脚本语言来说这就太复杂了。

2、JavaScript是单线程的但它所运行的宿主环境—浏览器是多线程,浏览器提供了各种线程供Event Loop调度来协调JS单线程运行时不会阻塞。

JS执行机制

主线程(宏任务) => 微任务 => 宏任务 => 主线程

如果宏任务里还有微任就继续执行宏任务里的微任务,如果宏任务中的微任务中还有宏任务就在依次进行

主线程任务——>微任务——>宏任务——>宏任务里的微任务——>宏任务里的微任务中的宏任务——>直到任务全部完成


---