先放一个实例出来:
下面的代码会打印出什么?
console.log(9)
setTimeout(() => {
console.log(1)
let promise2 = new Promise(function (resolve) {
console.log(5)
resolve()
}).then(function () {
console.log(6)
})
})
let promise1 = new Promise(function (resolve) {
console.log(2)
resolve()
}).then(function () {
console.log(3)
})
let promise3 = new Promise(function (resolve){
console.log(7)
resolve()
}).then(function () {
console.log(8)
})
console.log(4)
结果是 927438156
什么是栈、堆、序列
- 栈:有结构,存放数据有顺序,后进先出
- 序列:有结构,存放数据有顺序,先进先出
- 堆:没有结构,错放数据没有顺序
栈,堆,序列在js中的作用
-
由于stack有明确的数据结构和读取顺序,基本数据类型和对对象的引⽤都存在栈中,存在栈中的数据⼤⼩与⽣存期必须是确定的。可以明确知道每个区块的⼤⼩,因此,stack的寻址速度要快于heap。
-
heap⽤于复杂数据类型(引⽤类型)分配空间,例如数组对象、object对象。
-
stack创建的时候,⼤⼩是确定的,数据超过这个⼤⼩,就发⽣stack overflow错误,⽽heap的⼤⼩是不确定的,需要的话可以不断增加。stack在不同的浏览器中的⼤⼩限制不同。
-
js中函数的调⽤是按照栈调⽤的。⼀般chrome都能有效的GC(垃圾处理),但是循环引⽤会爆栈。
js内存中栈的堆对应关系
栈⾥函数执⾏的时候可能会调⼀些Dom操作,ajax操作和setTimeout定时器,这时候要等stack(栈)⾥⾯的所有程序先⾛(注意:栈⾥的代码是先进后出),⾛完后再⾛WebAPIs,WebAPIs执⾏后的结果放在callback queue(回调的队列⾥,注意:队列⾥的代码先放进去的先执⾏),也就是当栈⾥⾯的程序⾛完之后,再从任务队列中读取事件,将队列中的事件放到执⾏栈中依次执⾏,这个过程是循环不断的。
1.所有同步任务都在主线程上执⾏,形成⼀个执⾏栈。
2.主线程之外,还存在⼀个任务队列。只要异步任务有了运⾏结果,就在
任务队列之中放置⼀个事件。
3.⼀旦执⾏栈中的所有同步任务执⾏完毕,系统就会读取任务队列,将队列
中的事件放到执⾏栈中依次执⾏。
4.主线程从任务队列中读取事件,这个过程是循环不断的。
宏任务和微任务
- 宏任务(task)
浏览器为了能够使得JS内部task与DOM任务能够有序的执⾏,会在⼀个task执⾏结束后,在下⼀个task 执⾏开始前,对⻚⾯进⾏重新渲染 (task->渲染->task->…)⿏标点击会触发⼀个事件回调,需要执⾏⼀个宏任务,然后解析HTMl。
setTimeout的作⽤是等待给定的时间后为它的回调产⽣⼀个新的宏任务。 - 微任务(Microtasks )
微任务通常来说就是需要在当前 task 执⾏结束后⽴即执⾏的任务,⽐如对⼀系列动作做出反馈,或者是需要异步的执⾏任务⽽⼜不需要分配⼀个新的 task,这样便可以减⼩⼀点性能的开销。只要执⾏栈中没有其他的js代码正在执⾏且每个宏任务执⾏完,微任务队列会⽴即执⾏。如果在微任务执⾏期间微任务列加⼊了新的微任务,会将新的微任务加⼊队列尾部,之后也会被执⾏。微任务包括了promise的回调。
以上代码的解析过程:
Promise立即执行,then分发到微任务,排到当前宏任务的末尾执行
setTimeout是宏任务,在上一个宏任务执行完后执行
先执行栈中的9 2 7 4,然后是微任务的3 8,最后是1 5 6