面试题答案
一键面试- JavaScript事件循环基础
- JavaScript是单线程语言,这意味着它同一时间只能执行一个任务。事件循环机制让JavaScript能够处理并发操作。事件循环不断检查调用栈是否为空,当调用栈为空时,它会从任务队列中取出任务放入调用栈执行。
- Web API与事件循环的交互
- setTimeout:
- 当执行到
setTimeout
函数时,它的回调函数并不会立即进入调用栈。setTimeout
是由浏览器的Web API实现的。当setTimeout
被调用时,它的时间参数开始倒计时,在倒计时结束后,Web API将其回调函数放入宏任务队列。 - 例如:
- 当执行到
- setTimeout:
setTimeout(() => {
console.log('setTimeout callback');
}, 1000);
console.log('main script');
在这段代码中,首先输出main script
,因为它在主调用栈中直接执行。1秒后,setTimeout
的回调函数被放入宏任务队列,当调用栈为空时,该回调函数被取出并执行,输出setTimeout callback
。
- fetch:
fetch
也是Web API的一部分。当fetch
被调用时,它会发起一个网络请求。这个网络请求是异步的,不会阻塞JavaScript的执行。当网络请求完成(成功或失败),Web API会将fetch
的then
回调函数放入微任务队列(如果是链式调用then
)或者宏任务队列(如果是直接在fetch
调用后添加then
,不同浏览器可能有差异,现代浏览器倾向于微任务)。- 例如:
fetch('https://example.com/api')
.then(response => {
console.log('fetch success callback');
return response.json();
})
.catch(error => {
console.log('fetch error callback');
});
console.log('main script');
这里先输出main script
,当fetch
请求完成,then
回调函数进入队列,待调用栈为空时执行,输出fetch success callback
(假设请求成功)。
3. 宏任务和微任务混合时事件循环执行流程
- 宏任务队列:常见的宏任务包括
setTimeout
、setInterval
、I/O
操作、UI渲染
等。 - 微任务队列:常见的微任务包括
Promise.then
、MutationObserver
等。 - 执行流程:
- 首先执行全局的同步代码,将函数调用依次压入调用栈,执行完毕后清空调用栈。
- 检查微任务队列,如果有微任务,将微任务依次压入调用栈并执行,直到微任务队列为空。
- 然后检查宏任务队列,取出一个宏任务放入调用栈执行,执行完毕后再次检查微任务队列并清空微任务队列。
- 如此循环,不断从宏任务队列取任务执行,执行完后清空微任务队列,这个过程就是事件循环。
- 例如:
setTimeout(() => {
console.log('setTimeout macro task');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise micro task');
});
console.log('main script');
首先输出main script
,然后清空微任务队列,输出Promise micro task
,最后执行宏任务,输出setTimeout macro task
。