1. 什么是Event Loop
js代码在执行过程中先执行同步任务, 遇到异步任务则将任务放入对应的任务队列中,当同步任务执行完毕之后,将微任务从队列中调入主线程执行,微任务执行完毕后,将宏任务从队列中调入主线程执行,重复以上操作,直到所有任务执行完毕(事件循环)
2. 为什么会出现Event Loop
javascript是单线程,只有等前一个任务执行完毕,才能执行后一个 任务。一旦遇到任务执行时间较长,就会导致后续任务的延迟,而Event Loop就是解决单线程运行时不会阻碍的一种机制(异步的原理)。
3. 同步任务和异步任务
同步任务:在调用栈(执行栈)中等待主线程依次执行
异步任务:分为宏任务和微任务,在任务队列中等待主线程空闲的时候,进入栈中等待主线程执行
4. 执行上下文和执行栈
4.1 执行上下文
- 执行上下文类型
- 全局执行上下文 — 只有一个
- 函数执行上下文 — 可以有多个
- eval函数执行上下文
- 生命周期
- 创建阶段 — 确定 this 指向、生成变量对象、建立作用域链
- 执行阶段 — 变量赋值、函数引用、执行其他代码
- 销毁阶段 — 执行完毕出栈,等待回收被销毁
- this指向
- 全局执行上下文this指向全局对象
- 函数执行上下文this指向调用对象
- 示例
首先进入全局环境,创建全局执行上下文环境加入栈底。add2()被调用,创建对应的函数执行上下文环境并加入栈中。接着add()被调用,创建对应的函数执行上下文环境加入栈中。执行console.log(val + 12)代码,add函数执行完毕出栈,add2函数也执行完毕出栈,最后弹出全局执行上下文。
var num = 12
function add(val = 1) {
console.log(val + 12)
}
function add2() {
add(10)
}
add2()
4.2 执行栈
也叫调用栈,遵循后进先出原则,负责管理多个执行上下文。
5. 常见的宏任务和微任务有哪些?
5.1 宏任务
- setTimeout
- setInterval
- i/o
- 异步ajax
- …
5.2 微任务
- Promise.then
- Promise.catch
- Promise.finally
- async/await
- …
6. 示例
判断以下代码输出顺序
- 示例1
// 注意Promise参数里的回调函数不属于微任务,只有Promise.then里面的回调函数才属于微任务
console.log("script start")
let promise = new Promise((resolve) => {
console.log('promise start')
resolve("promise end")
}).then(res => {
console.log(res)
}).finally(() => {
console.log("promise finally")
})
console.log("script end")
script start —> promise start —> script end —> promise end —> promisie finally
- 示例2
console.log("script start")
let promise1 = new Promise((resolve) => {
console.log('promise start')
resolve('promise end')
}).then(res => {
console.log(res)
new Promise(resolve => {
console.log("promise2 start");
resolve("promise2 end")
}).then (res => {
console.log(res);
})
}).finally(() => {
console.log("promise finally")
})
console.log("script end")
script start —> promise start —> script end —> promise end —> promise2 start —> promise2 end —> promisie finally
- 示例3
// async函数返回一个Promise对象,函数遇到await就会先返回,等到触发的异步操作完成,在执行await后面的语句
console.log("script start")
async function async1() {
console.log("async1 start")
await async2()
console.log("async1 end")
}
function async2() {
console.log("async2 start")
}
async1()
console.log("script end")
script start —> async1 start —> async2 start —> script end —> async1 end
- 示例4
console.log("script start")
async function async1() {
console.log("async1 start")
return async2()
}
function async2() {
console.log("async2 start")
return "async2 end"
}
async1().then(res => {
console.log(res)
})
console.log("script end")
script start —> async1 start —> async2 start —> script end —> async2 end
- 示例5
console.log("script start")
setTimeout(() => {
console.log("setTimeout one")
}, 2000)
setTimeout(() => {
console.log("setTimeout two")
}, 1000)
console.log("script end")
script start —> setTimeout two —> setTimeout one —> script end
- 示例6
console.log("script start")
setTimeout(() => {
setTimeout(() => {
console.log("setTimeout one")
}, 0)
console.log("setTimeout two")
}, 2000)
function promise() {
console.log("function start")
return new Promise(resolve => {
console.log("promise start")
resolve("promise end")
})
}
promise().then(res => {
console.log(res)
})
console.log("script end")
script start —> function start —> promise start —> script end —> promise end —> setTimeout two —> setTimeout one