跳到主要内容

工具✅

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)
}

延伸阅读: