宏任务:setTimeout,setInterval,setImmediate,I/O,UI渲染,网络请求
微任务:Promise,process.nextTick,MutationObserver、async/await
-
首先,
js
是单线程的,主要的任务是处理用户的交互,而用户的交互无非就是响应DOM
的增删改,使用事件队列的形式,一次事件循环只处理一个事件响应,使得脚本执行相对连续,所以有了事件队列,用来储存待执行的事件,那么事件队列的事件从哪里被push进来的呢。那就是另外一个线程叫事件触发线程
做的事情了,他的作用主要是在定时触发器线程、异步HTTP请求线程满足特定条件下的回调函数push到事件队列中,等待js引擎空闲的时候去执行,当然js引擎执行过程中有优先级之分,首先js引擎在一次事件循环中,会先执行js线程的主任务,然后会去查找是否有微任务microtask(promise)
,如果有那就优先执行微任务,如果没有,在去查找宏任务macrotask(setTimeout、setInterval)
进行执行 -
众所周知
JS
是⻔⾮阻塞单线程语⾔,因为在最初 JS 就是为了和浏览器交互⽽诞⽣的。如果 JS 是⻔多线程的语⾔话,我们在多个线程中处理DOM
就可能会发⽣问题(⼀个线程中新加节点,另⼀个线程中删除节点),当然可以引⼊读写锁解决这个问题。 -
JS 在执⾏的过程中会产⽣执⾏环境,这些执⾏环境会被顺序的加⼊到执⾏栈中。如果遇到异步的代码,会被挂起并加⼊到 Task (有多种 task ) 队列中。⼀旦执⾏栈为空,Event Loop 就会从 Task 队列中拿出需要执⾏的代码并放⼊执⾏栈中执⾏,所以本质上来说 JS 中的异步还是同步⾏为
console.log('script start'); setTimeout(function () { console.log('setTimeout'); }, 0); console.log('script end');
以上代码虽然 setTimeout
延时为 0 ,其实还是异步。这是因为 HTML5 标准规定这个函数第⼆个参数不得⼩于 4 毫秒,不⾜会⾃动增加。所以 setTimeout 还是会在script end 之后打印。
JS 在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异步的代码,会被挂起并加入到 Task(有多种 task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为.
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务( microtask )和 宏任务( macrotask )。在 ES6 规范中, microtask 称为 jobs, macrotask 称为task 。
console.log('1111');
setTimeout(function () {
console.log('6666666');
}, 0);
new Promise((resolve) => {
console.log('222222')
resolve()
}).then(function () {
console.log('44444444');
}).then(function () {
console.log('555555555');
});
console.log('333333');
Event loop
顺序
- 执行同步代码,这属于宏任务
- 执行栈为空,查询是否有微任务需要执行
- 执行所有微任务
- 必要的话渲染
UI
- 然后开始下一轮
Event loop
,执行宏任务中的异步代码
setImmediate,I/O,process.nextTick,MutationObserver
以下是使用 setImmediate
、I/O
、process.nextTick
和 MutationObserver
的示例代码:
// 使用 setImmediate 进行 I/O
console.log('在 setImmediate 之前');
setImmediate(() => {
console.log('setImmediate 回调函数');
});
console.log('在 setImmediate 之后');
// 使用 process.nextTick 进行即时变异
console.log('在 process.nextTick 之前');
process.nextTick(() => {
console.log('process.nextTick 回调函数');
});
console.log('在 process.nextTick 之后');
// 使用 MutationObserver 进行 DOM 变异监测
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
console.log('DOM 变异:', mutation);
});
});
const target = document.querySelector('#target');
const config = { childList: true };
observer.observe(target, config);
// 修改 DOM 触发变异
console.log('在 DOM 变异之前');
target.innerHTML = '<p>新的内容</p>';
console.log('在 DOM 变异之后');
在这段代码中:
- 使用
setImmediate
进行I/O
操作后的回调函数的调度。 - 使用
process.nextTick
进行即时变异,即在当前操作完成后立即执行的回调函数的调度。 - 使用
MutationObserver
监测和响应DOM
的变化,例如JavaScript
所进行的变异。
请注意,setImmediate
和 process.nextTick
是 Node.js
特定的功能,而 MutationObserver
是在浏览器环境中使用的,并且需要实际的 HTML
文档来观察 DOM
变异。
当我们希望监测 DOM
变化并在变化发生时执行相应的操作时,可以使用 MutationObserver
。下面是一个使用 MutationObserver
的简单案例:
// 目标元素
const target = document.querySelector('#target');
// 创建 MutationObserver 实例
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
console.log('观察到 DOM 变化:', mutation);
// 在这里可以执行相应的操作
// 例如,可以使用 mutation.target 获取目标元素,对其进行相应的处理
});
});
// 配置对象,定义我们希望观察的变化类型
const config = { childList: true, subtree: true, attributes: true };
// 开始观察目标元素的变化
observer.observe(target, config);
// 修改 DOM 触发变化
target.textContent = '新的文本内容';
// 停止观察变化
observer.disconnect();
在这个案例中:
- 我们首先通过
document.querySelector
方法选择了目标元素,这个目标元素可以是任何你希望观察变化的DOM
元素。 - 我们创建了一个
MutationObserver
实例,传入了一个回调函数作为参数。当指定的变化类型发生时,这个回调函数将会被调用。 - 我们定义了一个配置对象,其中
childList: true
表示观察子元素的增加或删除,subtree: true
表示观察目标元素的所有后代元素,attributes: true
表示观察属性的变化。 - 通过调用
observer.observe(target, config)
方法,开始观察目标元素的变化。 - 我们修改了目标元素的文本内容,这会触发变化并导致观察者的回调函数被调用。
- 最后,我们可以通过调用
observer.disconnect()
方法停止观察变化。
在回调函数中,你可以根据变化的具体情况执行相应的操作,例如更新 UI
、触发其他功能等。
process
Node.js 中的 process
全局对象提供了许多与当前 Node.js 进程有关的属性和方法。下面是一些常用的 process
对象的属性:
-
process.argv
:一个包含命令行参数的数组。第一个元素是 Node.js 的可执行文件路径,第二个元素是当前执行的 JavaScript 文件路径,后续元素是传递给脚本的命令行参数。 -
process.env
:一个包含当前进程环境变量的对象。可以使用它来获取或设置环境变量的值。 -
process.cwd()
:返回当前工作目录的路径。 -
process.pid
:返回当前进程的标识符。 -
process.platform
:返回 Node.js 运行的操作系统平台。 -
process.exit(code)
:以指定的退出码退出当前进程。 -
process.uptime()
:返回 Node.js 进程的运行时间,以秒为单位。 -
process.version
:返回当前 Node.js 的版本号。 -
process.arch
:返回当前 Node.js 进程的处理器架构。 -
process.title
:获取或设置进程的名称。
这些只是 process
对象的一些常见属性,还有其他的一些属性和方法,可根据需要在代码中进一步探索和使用。可以通过查阅官方文档来获取更详细的信息:Node.js process 文档。