JavaScript 的 EventLoop,宏任务,微任务
js 中的线程
js为什么是单线程
js单线程的特点就是同一时刻只能执行一个任务
js单线程主要由于一些用户的互动以及操作dom等相关的操作决定了js要使用单线程,否则使用多线程会带来复杂的同步问题.
如果一个线程正在修改dom,另一个线程在删除dom,那么以哪个为准?
如果执行同步问题的话,多线程需要加锁,执行任务会非常繁杂
虽然
HTML5
标准规定,允许javascript
脚本创建多个线程,但是子线程完全受主线程控制,且不可以操作dom
单线程带来的问题
阻塞,当一个任务执行完成之后才能执行下一个任务 ,这样就会导致页面卡死状态,页面无响应,影响用户体验,所以不得不出现同步任务和异步任务的解决方案.
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步任务:不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,才会将进入主线程执行
js如何实现异步编程
- 所有同步任务都在主线程,形成一个执行栈
- 主线程外还存在一个
任务队列
,只要异步任务有运行结果,就是任务队列
之中放置一个事件 - 一旦执行栈那种所有同步任务执行完毕,系统就读取
任务队列
,看看里面哪些异步任务执行完毕,进入执行栈开始执行 - 主线程不断重复上述三步
EventLoop
几个概念:
- 执行上下文(
Execution context
)- 执行栈(
Execution stack
)- 微任务(
micro-task
)- 宏任务(
macro-task
)
执行上下文
函数执行前进行的准备工作(也称执行上下文环境)
运行
javascript
代码时候,当代码进入一个环境时,就会为改环境创建一个执行上下文,他会在你运行代码前做一些准备,例如确定作用域,创建局部变量对象等
javascript执行上下文类型
- 全局环境:代码首次执行时的默认环境
- 函数环境:每当执行流程进入到一个函数体内部的时候
Eval
函数:执行eval
函数内部的代码也会有它属于自己的执行上下文
执行栈
数据结构中的栈,
先进后出
let a = "1"
function first() {
console.log("first 1")
second()
console.log("first 2")
}
function second() {
console.log("second");
}
first()
console.log("执行全局");
- 初始化状态,执行栈任务为空;
fist()
函数执行,first()
进入执行栈,输入"first 1",碰到函数second()
second()
进入执行栈,开始执行second()
函数,输出"second"second()
函数执行完出栈,继续执行栈顶端的函数first()
,输出"first 2"first()
出栈,执行下面输出"执行全局"
事件循环
宏任务
可以理解为每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)
浏览器为了能使得js内部宏任务与
DOM
任务有序的执行,会在一个宏任务执行结束后,在下一个宏任务执行开始前,对页面重新渲染(宏任务=>渲染=>宏任务=>…)
宏任务包括
- script(整体代码)
- setTimeout
- setInterval
- I/O
- UI交互事件
- postMessage
- MessageChannel
- setImmediate(nodejs)
微任务
可以理解为当前宏任务执行结束后立即执行的任务
微任务包括
- Promise.then()
- Object.observe
- MutaionObserver
- process.nextTick
宏任务微任务运行机制
宏任务和微任务
主线程上添加宏任务与微任务
console.log("start");
setTimeout(() => {
console.log("setTimeout");
});
new Promise((resolve) => {
console.log("promise");
resolve("成功");
}).then((res) => {
console.log(res);
});
console.log("end");
[结果]:
start
promise
end
成功
setTimeout
执行顺序:主线程=>主线程上创建的微任务=>主线程上创建的宏任务
微任务中创建微任务
console.log('1');
setTimeout(() => {
console.log('2');
})
new Promise(resolve => {
resolve()
console.log('3');
}).then(() => {
console.log('4');
Promise.resolve().then(() => {
console.log('5');
}).then(() => {
console.log('6');
})
})
console.log('7');
[结果]:
1
3
7
4
5
6
2
执行顺序:主线程=>主线程上创建的微任务=>微任务上创建的微任务=>主线程上创建的宏任务
同时两个微任务执行
console.log('1');
setTimeout(() => {
console.log('2');
})
new Promise(resolve => {
console.log('3');
resolve()
}).then(() => {
console.log('4');
Promise.resolve().then(() => {
console.log('5');
}).then(() => {
console.log('6');
})
}).then(() => {
console.log('7');
})
console.log('8');
[结果]:
1
3
8
4
5
7
6
2
宏任务中创建微任务
console.log('1');
setTimeout(() => {
console.log('2');
setTimeout(() => {
console.log('3');
})
new Promise(resolve => {
console.log('4');
resolve()
}).then(() => {
console.log('5');
})
})
setTimeout(() => {
console.log('6');
})
console.log('7');
[结果]:
1
7
2
4
5
6
3
微任务中创建宏任务
console.log('1');
new Promise(resolve => {
console.log('2');
resolve()
}).then(() => {
console.log('3');
setTimeout(() => {
console.log('4');
})
})
setTimeout(() => {
console.log('5');
})
console.log('6');
[结果]:
1
2
6
3
5
4
评论区