本文旨在使用
javascript的事件循环(event loop)机制,抛砖引玉Jest中的 Timer 事件机制
来聊聊 javascript的事件循环 ( event loop )
首先, js 的事件循环 (之后就简称 event loop) 是 js 的运行机制之一,它是一个基于事件驱动的机制,用于管理未来执行的代码。
js 的 event loop 是单线程。这是说它在同一个时间只能做一件事情。一心一意。
event loop 的核心是事件队列。它是一个规则,制定了不同身份的事件成员,除去先来后到的原则之外,还有 VIP 机制。当一个事件被触发的时候,它就被加入事件队列中,等待 js 引擎去处理。它大概的过程是这样的:
- 事件队列中,包含很多的小队,第一小队排队的第一个事件开始
- 让这个事件进入大厅,去登记,办事。。。
- 如果这个事件中有些大厅办不了,比如:他是个异步VIP,那么它就会被单独邀请去 异步的API 小厅处理。因为他重要,所有之前大厅负责他的工作人员,跟着去陈述客户需求了,所以大厅的这个窗口只有等这个人处理结束
- 当VIP 事件处理完成后,继续回到大厅处理属于大厅的业务,直至这个事儿完了
- 接下来,再去重新进一个事件
请自行脑补银行窗口排队办业务。这个流程大概就是整个事件循环的过程。
下面举一些js事件循环的插队事件示例:
// 示例代码一:setTimeout
console.log("Start");
setTimeout(function() {
console.log("Inside setTimeout");
}, 0);
console.log("End");
// 输出结果:Start -> End -> Inside setTimeout
// 示例代码二:Promise
console.log("Start");
let promise = new Promise(function(resolve, reject) {
console.log("Inside Promise");
resolve("Promise Resolved");
});
promise.then(function(value) {
console.log(value);
});
console.log("End");
// 输出结果:Start -> Inside Promise -> End -> Promise Resolved
来聊聊正式内容: Jest的 event loop
首先,说说 Jest
Jest 是一个流行的测试框架。它本身提供了许多工具,API帮我们编写测试用例。 这里是 Jest 官网:jestjs.io/docs/gettin…
其次,说说 Timer
Jest内置的一项为 Timer, 它本身提供了 setTimeout、setInterval 和 setImmediate 等 API 来实现异步操作。这些 API 在执行时会调用 JavaScript 的事件循环机制,以便在指定的时间后执行回调函数。
Jest Timer 内置了一个 event loop,它实现了类似于浏览器中的事件循环机制。当使用 Jest Timer API 时,它会将回调函数放入事件队列中,并在指定的时间后执行。与浏览器中的事件循环机制类似,Jest Timer 的事件循环机制也是单线程的,即只能处理一个任务。当 Jest Timer API 调用完毕后,它会将回调函数放入事件队列中,并等待 JavaScript 引擎去处理。
再次,两个事件机制的比对
-
相似点- 都是单线程
- 都需要在事件队列中排队
- 都是 js 引擎处理
-
不同点-
事件循环机制不同:
- js 是浏览器或者 nodejs引擎的内部机制
- Jest Timer 是 Jest 框架中的机制,用于测试用例
-
回调函数的执行时间不用:
- js的执行时间,是由事件队列中的排队顺序,以及 js 引擎自身的执行效率决定的
- Jest Timer 的执行时间可以指定
-
其他等
- js 是用来写代码的
- jest 是用来测试 js 代码的
-
举个Jest Timer🌰
// 示例代码:使用 Jest Timer API 模拟异步操作
test("Jest Timer API Test", () => {
expect.assertions(1);
return new Promise(resolve => {
setTimeout(() => {
expect(1 + 2).toBe(3);
resolve();
}, 1000); // 这里设置的时间为 1 秒
});
});
// 示例代码:使用 Jest Mock API 模拟异步操作
test("Jest Mock API Test", () => {
const callback = jest.fn();
setTimeout(() => {
callback();
}, 1000); // 这里设置的时间为 1 秒
jest.advanceTimersByTime(1000); // 快进时间
expect(callback).toHaveBeenCalled();
});
这里提一下,在 Jest Timer 内置的事件循环机制中,计时器的时间精度是毫秒级别的。如果需要更高的时间精度,可以使用 jest.useFakeTimers() 函数来覆盖 Jest 的默认计时器实现。
一般我们需要精确的断言异步的时间,去做一些断言的时候,会这么做:
// 使用 jest 生命周期
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
jest.clearAllTimers();
cleanup();
});
// 实际断言代码中
test('it is a demo', async () => {
// ...
await act(async () => jest.advanceTimersByTime(6300));
// ...
});
从而能在期盼的事件内,断言一些正确的数据表象。
总结一下
总的来说,JavaScript 事件循环机制和 Jest Timer 内置的事件循环机制都是基于事件驱动的机制,用于管理未来执行的代码。它们都是单线程的,需要将事件放入事件队列中,并等待 JavaScript 引擎去处理。虽然它们的实现方式略有不同,但都是为了实现异步操作和提高代码的性能和效率。
其他:有关于事件驱动和机制的一些示例
事件驱动的理念和机制示例
// 示例代码:使用事件监听器监听按钮点击事件
let button = document.querySelector("button");
button.addEventListener("click", function() {
console.log("Button clicked!");
});
在事件驱动的机制中,事件监听器会监听某个事件的发生,并在事件发生时执行相应的回调函数。在示例代码中,我们使用事件监听器 addEventListener 来监听按钮的点击事件,当按钮被点击时,回调函数将被执行。这种事件驱动的机制可以使代码更加简洁和易于理解,同时也能够处理复杂的异步操作。
事件队列中事件的顺序和 JavaScript 引擎的执行效率示例
// 示例代码:演示事件队列中事件的顺序和 JavaScript 引擎的执行效率
console.log("Start");
setTimeout(function() {
console.log("Inside setTimeout 1");
}, 0);
setTimeout(function() {
console.log("Inside setTimeout 2");
}, 0);
Promise.resolve().then(function() {
console.log("Promise resolved");
});
console.log("End");
// 输出结果:Start -> End -> Promise resolved -> Inside setTimeout 1 -> Inside setTimeout 2
在示例代码中,我们使用了 setTimeout 和 Promise 两种方式来模拟异步操作,并在回调函数和 .then() 方法中输出相应的内容。在代码执行过程中,JavaScript 引擎会先执行同步代码,然后再执行异步代码。而在异步代码中,Promise 对象的 .then() 方法比 setTimeout 的回调函数先被推入事件队列中,所以 Promise 的回调函数会比 setTimeout 的回调函数先被执行。另外,由于 setTimeout 的回调函数的时间都被设置为 0,所以它们会在相同的时间被推入事件队列中,但它们的执行顺序是不确定的,取决于 JavaScript 引擎的执行效率。在这个例子中,我们可以看到,Promise 的回调函数被先执行,然后是 setTimeout 的回调函数。
Node.js 中的事件驱动机制示例
// 示例代码:使用 Node.js 中的事件驱动机制读取文件内容
const fs = require("fs");
let readStream = fs.createReadStream("file.txt");
readStream.on("data", function(data) {
console.log("Received data: ", data.toString());
});
readStream.on("end", function() {
console.log("Finished reading file.");
});
在 Node.js 中,事件驱动机制被广泛应用于 I/O 操作等异步操作中。在示例代码中,我们使用 Node.js 内置的 fs 模块创建了一个可读流 readStream,并监听了两个事件 data 和 end。当 data 事件被触发时,回调函数会输出读取到的数据,而当 end 事件被触发时,回调函数会输出读取文件结束的信息。这种事件驱动的机制使得我们可以使用异步操作来读取大文件,而不会阻塞主线程的执行。
React 中的事件驱动机制示例
// 示例代码:使用 React 中的事件驱动机制监听按钮点击事件
import React from "react";
class Button extends React.Component {
handleClick = () => {
console.log("Button clicked!");
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
在 React 中,事件驱动机制被应用于组件的交互操作中。在示例代码中,我们创建了一个按钮组件 Button,并在 render() 方法中使用 JSX 语法将其渲染到页面上。在按钮的 onClick 事件中,我们使用箭头函数定义了一个回调函数 handleClick,用于处理按钮被点击时的操作。当按钮被点击时,该回调函数将被执行。这种事件驱动的机制可以使得组件之间的交互更加灵活和高效。