前端入栈和出栈
在编程中,事件入栈(Event Pushing)和出栈(Event Popping)通常与事件循环(Event Loop)和消息队列(Message Queue)相关。这些概念在前端开发中尤其重要,特别是在处理异步事件和回调函数时。下面我将解释这些概念,并提供一些代码示例。
事件循环(Event Loop)
事件循环是JavaScript运行时环境的一部分,它负责协调事件、用户交互、脚本执行、渲染、网络请求等。JavaScript是单线程语言,事件循环使得它能够执行非阻塞操作。
消息队列(Message Queue)
消息队列是一个先进先出(FIFO)的数据结构,用于存储待处理的事件。这些事件可能是用户交互(如点击、滚动等)、定时器超时、网络请求完成等。
事件入栈(Event Pushing)
当一个事件发生时,如用户点击了一个按钮,这个事件会被创建并放入消息队列的末尾。这个过程称为事件入栈。
// 假设这是一个用户点击事件的处理函数
function handleClick() {
console.log('Button clicked!');
}
// 用户点击按钮时,handleClick事件被放入消息队列
button.addEventListener('click', handleClick);
事件出栈(Event Popping)
事件循环会不断检查消息队列。如果队列非空,并且主线程(当前执行栈)空闲,事件循环会将消息队列中的第一个事件出栈,并执行其回调函数。
// 事件循环的简化示意
while (true) {
if (messageQueue.isNotEmpty() && mainThread.isIdle()) {
let event = messageQueue.dequeue(); // 出栈
event.callback(); // 执行回调
}
}
示例
下面是一个简单的例子,演示了定时器事件如何入栈和出栈:
console.log('Script start');
setTimeout(function timeoutCallback() {
console.log('Timeout occurred');
}, 0);
console.log('Script end');
// 输出顺序:
// Script start
// Script end
// Timeout occurred
在这个例子中,即使setTimeout
的延迟时间设置为0,回调函数timeoutCallback
也不会立即执行。首先,当前的执行栈(同步代码)必须完成执行,即打印"Script start"和"Script end"。然后,事件循环会从消息队列中取出定时器事件,并执行其回调函数,打印"Timeout occurred"。
希望这些解释和示例能帮助你理解事件入栈和出栈的概念。
实战:
export const stack:any = []
export function executeFunctionFromString(functionString?:string, args?: any[]) {
if (functionString) {
if ((window as any).log) {
try {
var fn = eval("(" + functionString + ")");
fn(...(args || []));
} catch (error) {
}
} else {
stack.push(function() {
var fn = eval("(" + functionString + ")");
fn(...(args || []));
});
function executeStack() {
if (stack.length > 0) {
setTimeout(function() {
if ((window as any).log) {
// 出栈并执行
let fn = stack.pop();
fn();
} else {
executeStack()
}
}, 1000);
}
}
executeStack()
}
}
}
这个场景是需要通过npm传递函数,所以把函数转换成字符串,接受后再转换成函数。但出现一个问题,就是执行的需要依赖log,但是log又需要出示化后才能用,所以就把事件入栈,等1秒后在执行,如果有log就执行然后事件出栈,没有的话在等1秒后执行。