1.执行栈:
showName()
console.log(myname)
var myname = 'JS执行栈'
function showName() {
console.log('函数showName被执行');
}
执行结果:
函数showName被执行
undefined
原因:
JS代码执行过程为:
编译阶段:生成执行上下文和可执行代码。
具体以这段代码为例:
showName()
console.log(myname)
var myname = 'JS执行栈'
function showName() {
console.log('函数showName被执行');
}
//变量环境
var myname = undefined;
function showName() {
console.log('函数showName被执行');
}
//可执行代码
showName();
console.log(myname);
myname = 'JS执行栈'
任务循环:以单线程的JavaScript为基础,在管理多任务的同时,兼顾代码执行的效率和事件响应的的实时性。
2.JavaScript任务循环基本模型
JavaScript单线程:
在上面的执行代码中,我们把所有任务代码按照顺序写进主线程里,等线程执行时,这些任务会按照顺序在线程中依次被执行;等所有任务执行完成之后,线程会自动退出。可以参考下图来直观地理解下其执行过程:
线程内部事件处理:
要想在线程运行过程中,能接收并执行新的任务,就需要采用事件循环机制。
线程间事件处理:
消息队列:一种数据结构,可以存放要执行的任务。它符合队列“先进先出”的特点,也就是说要添加任务的话,添加到队列的尾部;要取出任务的话,从队列头部去取。
添加一个消息队列;
IO 线程中产生的新任务添加进消息队列尾部;
渲染主线程会循环地从消息队列头部中读取任务,执行任务。
进程间事件处理:
渲染进程专门有一个 IO 线程用来接收其他进程传进来的消息,接收到消息之后,会将这些消息组装成任务发送给渲染主线程,后续的步骤就和前面讲解的“处理其他线程发送的任务”一样了。
3.任务分类:
任务分类目的:平衡当前任务的执行效率和监控的实时性。
实时性:
如果将当前产生的任务都放在任务队列的尾部,则又会影响一些优先级较高的任务执行。即:当触发一个任务后,过了可能会过很长时间才执行,前面不太紧急的任务占用了大量时间。
效率:
如果当前任务一产生新的任务就立即执行,那么如果产生的新任务执行时间较长,就会阻塞当前任务的执行。
宏任务:
消息队列中的任务称为宏任务,每个宏任务中都包含(维护)了一个微任务队列,在执行宏任务的过程中,如果 DOM 有变化,那么就会将该变化添加到微任务列表中,(如果产生了优先级较高的任务,就将该任务以微任务的方式添加到微任务队列)这样就不会影响到宏任务的继续执行,因此也就解决了执行效率的问题。
微任务:
等宏任务中的主要功能都直接完成之后,这时候,渲染引擎并不着急去执行下一个宏任务,而是执行当前宏任务中的微任务,因为 DOM 变化的事件都保存在这些微任务队列中(我们在执行宏任务的同步代码时,将优先级较高的任务添加到了微任务队列中),这样也就解决了实时性问题。
宏任务包含(产生宏任务的方式):
渲染事件(如解析 DOM、计算布局、绘制);
用户交互事件(如鼠标点击、滚动页面、放大缩小等);
JavaScript 脚本执行事件;网络请求完成、文件读写完成事件。
主代码块
setTimeout
setInterval
setImmediate ()-Node
requestAnimationFrame ()-浏览器
…
微任务包含(产生微任务的方式):
process.nextTick ()-Node
Promise.then()
catch
finally
Object.observe
MutationObserver
…
4.执行时机:
宏任务:在任务队列中排队执行。
微任务:在当前任务的同步代码执行完之后,任务结束前(下一个宏任务开始执行前)。
5.示例:
function fun1() {
setTimeout(() => {
console.log('timeout-1')
}, 0)
console.log('test-1')
}
fun1()
function fun2() {
Promise.resolve().then(() => {
console.log('promise-1')
})
console.log('test-2')
}
fun2()
function fun3() {
setTimeout(() => {
console.log('timeout-3')
})
Promise.resolve().then(() => {
console.log('promise-3')
})
console.log('test-3')
}
fun3()
let p4 = new Promise((resolve, rejest) => {
console.log('创建promise-4')
})
function fun4() {
p4.then(() => {
console.log('p4-then')
})
console.log('test-4')
}
fun4()
let p5 = new Promise((resolve, rejest) => {
console.log('创建promise-5')
setTimeout(() => {
resolve()
},3000)
})
function fun5() {
p5.then(() => {
console.log('p5-then')
})
console.log('test-5')
}
fun5()
console.log('1');
setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})