Appearance
防重复执行的机制
本节目标:理解“防重复执行的机制”的核心思路,并能把它用于实际开发或面试表达。
说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 选择立即执行函数的原因
- 代码简洁:不需要额外的变量名
- 作用域清晰:临时函数不会污染外部作用域
- 一次性使用:这个函数只用一次,不需要重复引用
防重复执行的机制
1. 状态检查层
typescript
const init = async (opts = {}) => {
// 第一道防线:已初始化检查
if (isLoaded.value && !opts.force) return
// 第二道防线:正在初始化检查
if (_initPromise && !opts.force) return _initPromise
// ...
}2. Promise缓存层
typescript
// 关键:缓存Promise实例
let _initPromise: Promise<void> | null = null
_initPromise = (async () => {
// 立即执行的异步函数
try {
await fetchAccountData()
} finally {
_initPromise = null // 完成后清理
}
})()
return _initPromise // 返回同一个Promise并发场景演示
关键机制分解
时间线分析:
T1时刻(组件A调用):
typescript
// 第一次调用
if (isLoaded.value && !opts.force) return // false,继续
if (_initPromise && !opts.force) return _initPromise // _initPromise=null,继续
// 创建并立即执行
_initPromise = (async () => {
// 开始执行异步操作
await fetchAccountData() // 耗时操作
})()T2时刻(组件B调用,T1还在执行中):
typescript
// 第二次调用
if (isLoaded.value && !opts.force) return // false,继续
if (_initPromise && !opts.force) return _initPromise // _initPromise存在!直接返回为什么立即执行很重要?
如果不用立即执行:
typescript
// ❌ 错误做法
_initPromise = async () => {
await fetchAccountData()
}
// 这只是函数定义,没有执行!用立即执行:
typescript
// ✅ 正确做法
_initPromise = (async () => {
await fetchAccountData() // 立即开始执行
})()
// 返回的是正在执行的Promise实例完整流程验证
让我写个简化版本来演示:
typescript
let _initPromise = null
let isLoaded = false
const init = async () => {
console.log('init调用', Date.now())
if (isLoaded) return console.log('已加载,跳过')
if (_initPromise) return console.log('复用Promise')
console.log('开始初始化')
_initPromise = (async () => {
console.log('执行异步操作')
await new Promise((resolve) => setTimeout(resolve, 1000))
isLoaded = true
console.log('初始化完成')
})()
await _initPromise
_initPromise = null
}
// 测试并发调用
init() // 第一次:开始执行
init() // 第二次:复用Promise
init() // 第三次:复用Promise总结
防重复的关键在于:
- 状态标志:
isLoaded防止已完成的重复调用 - Promise缓存:
_initPromise防止进行中的重复执行 - 立即执行:确保第一次调用时真正开始异步操作
- 共享结果:后续调用返回同一个Promise,等待相同结果 这样无论多少个组件同时调用
init(),实际的API请求只会发送一次!
