跳到主要内容

工具✅

nextTick/setImmediate/setTimeout 区别 ?

答案

核心概念:

  • process.nextTick 将回调安排在“当前调用栈清空后、事件循环继续前”执行,优先级高于 Promise microtask 与各阶段回调。
  • 常用于:修正同步/异步一致性(避免 Zalgo)、打断递归堆栈、在返回前补充清理或回调。
  • 与 setImmediate/setTimeout 不同:nextTick 不进入下一轮事件循环;大量排队会饿死 I/O,应谨慎使用。
  • 顺序要点:每个阶段回调结束后→先清空 nextTick 队列→再清空微任务(Promise)→再进入下一阶段。
提示

需要注意在 ESM 模块中由于是异步加载所以 Promise 会在 nextTick 之前执行。此外出现嵌套 Promise 时,会先执行完 Promise 队列再清空 nextTick 队列。

setImmediate 是在下一个事件循环 (event loop) 周期执行,如果在 IO 事件中由于 setImmediate 会处理阶段在 setTiemout 之后所以在 setTimeout 之前执行

延伸阅读

vm 有什么作用?

答案

核心概念:

  • vm 提供“在受控上下文中执行 JS”的能力:编译缓存(vm.Script)、隔离上下文(vm.createContext/runInContext)、本进程执行控制(timeout、breakOnSigint)。
  • 典型用途:在白名单沙箱里运行用户脚本/表达式、模板渲染求值、热更新代码评估、预编译脚本复用。
  • 隔离与注入:newContext 的 global 与宿主不同,默认无 process/require,需显式注入 console/Math 等白名单对象。
  • 安全边界:vm 不是强安全沙箱,面对不可信代码应优先用 worker_threads/child_process 做进程/线程级隔离。
  • 性能控制:复用 Script 可多次执行提升性能;通过 timeout 限制死循环;必要时配合信号中断。 。

示例说明:

// 基础:隔离上下文、Script 复用、超时防护
const vm = require('node:vm')

// 1. 创建一个沙箱对象,限制可访问的全局变量
const sandbox = { console, Math, Date }
vm.createContext(sandbox) // 隔离上下文环境

// 2. 创建可复用的脚本对象
const script = new vm.Script(`
   // 检查沙箱内是否能访问 process(通常不可访问)
   console.log('in sandbox -> typeof process:', typeof process)
   // 使用 globalThis 在沙箱内存储变量,实现计数
   globalThis.counter = (globalThis.counter || 0) + 1
   counter
`)

// 3. 在同一个沙箱上下文中多次运行脚本,计数器会累加
const r1 = script.runInContext(sandbox, { timeout: 50 }) // 第一次运行
const r2 = script.runInContext(sandbox, { timeout: 50 }) // 第二次运行
console.log('sandbox counter:', r1, '->', r2)

// 3. runInNewContext 创建新上下文并运行代码,返回函数
const double = vm.runInNewContext('x => x * 2', { /* 空上下文 */ })
console.log('runInNewContext fn(5)=', double(5))

// 4。 runInThisContext 在当前上下文运行代码,可访问主进程变量
const kind = vm.runInThisContext('typeof process')
console.log('in this context -> typeof process:', kind)

// 超时防护:运行死循环脚本,超时后抛出异常
try {
  new vm.Script('for(;;){}').runInContext(sandbox, { timeout: 20 })
} catch (e) {
  console.log('timeout caught:', e.code || e.name)
}

延伸阅读: