TS开发笔记
2024年12月26日大约 2 分钟
TS开发笔记
解决vscode报错:找不到“cookie”的类型定义文件。程序包含该文件是因为: 隐式类型库 "cookie" 的入口点ts
第一步:清理yarn缓存
yarn cache clean第二步:删除node_modules文件夹和yarn.lock
第三步:重新下载依赖
yarn install然后你就会发现报错已经消失辣~
VSCode TS报错:找不到div
照着StackoverFlow上的帖子,暂时解决了这个问题。
React + TS 异步函数竞态条件
useEffect中使用异步函数
下面这段代码是经典的useEffect中使用异步函数初始化的坑:
useEffect(() => {
let unregister: (() => void) | undefined
console.log("register")
const setup = async () => {
unregister = await TaskEventManager.register(
"init_workspace",
(payload) => {
switch (payload.stage) {
case "progress": {
updateToast(
payload.progress,
payload.message
)
break
}
case "increase_progress": {
console.log("increase_progress", payload.progress, payload.message)
console.log("current: ", progressRef.current + payload.progress!)
updateToast(
progressRef.current + (payload.progress ?? 0),
payload.message
)
break
}
case "done": {
break
}
case "error": {
break
}
}
}
)
}
setup()
return () => {
console.log("unregister: ", unregister)
unregister?.()
}
}, [])如果开启React.StrictMode,在控制台看到的打印大概率如下:
register
unregister: undefined
register应修改为:
useEffect(() => {
let unregister: (() => void) | undefined
let cancelled = false
console.log("register")
const setup = async () => {
unregister = await TaskEventManager.register(
const fn = await TaskEventManager.register(
"init_workspace",
(payload) => {
switch (payload.stage) {
case "progress": {
updateToast(
payload.progress,
payload.message
)
break
}
case "increase_progress": {
console.log("increase_progress", payload.progress, payload.message)
console.log("current: ", progressRef.current + payload.progress!)
updateToast(
progressRef.current + (payload.progress ?? 0),
payload.message
)
break
}
case "done": {
break
}
case "error": {
break
}
}
}
)
if (cancelled) {
// StrictMode 第一次 cleanup 之后才 resolve
fn()
console.log("unregister: ", fn)
} else {
unregister = fn
}
}
setup()
return () => {
cancelled = true
console.log("unregister: ", unregister)
unregister?.()
}
}, [])初始化中的竞态条件
这里的initialized未能正确地锁住两个线程的并发访问,会造成重复监听的问题:
type TaskHandler = (payload: TaskEventPayload) => void
class TaskEventManager {
private static unlistenFn: UnlistenFn | null = null
private static handlers: Map<string, Set<TaskHandler>> = new Map()
private static initialized = false
/**
* 初始化监听(只执行一次)
*/
static async init() {
if (this.initialized) return
this.unlistenFn = await listen<TaskEventPayload>(
"task-event",
(event) => {
const payload = event.payload
const taskHandlers = this.handlers.get(payload.task)
if (!taskHandlers) return
taskHandlers.forEach((handler) => {
handler(payload)
})
}
)
this.initialized = true
}
/**
* 注册任务监听
*/
static async register(task: string, handler: TaskHandler) {
await this.init()
if (!this.handlers.has(task)) {
this.handlers.set(task, new Set())
}
this.handlers.get(task)!.add(handler)
return () => {
this.handlers.get(task)?.delete(handler)
}
}
/**
* 移除所有监听
*/
static disposeAll() {
this.unlistenFn?.()
this.handlers.clear()
this.unlistenFn = null
this.initialized = false
}
}
export default TaskEventManager修改为:
type TaskHandler = (payload: TaskEventPayload) => void
class TaskEventManager {
private static unlistenFn: UnlistenFn | null = null
private static handlers: Map<string, Set<TaskHandler>> = new Map()
private static initialized = false
// 🔥 关键:用 Promise 作为初始化锁
private static initPromise: Promise<void> | null = null
/**
* 初始化监听(只执行一次)
*/
static async init() {
if (this.initialized) return
if (this.initPromise) {
return this.initPromise
}
this.initPromise = (async () => {
this.unlistenFn = await listen<TaskEventPayload>(
"task-event",
(event) => {
const payload = event.payload
const taskHandlers = this.handlers.get(payload.task)
if (!taskHandlers) return
taskHandlers.forEach((handler) => {
handler(payload)
})
}
)
})()
this.initialized = true
return this.initPromise
}
/**
* 注册任务监听
*/
static async register(task: string, handler: TaskHandler) {
await this.init()
if (!this.handlers.has(task)) {
this.handlers.set(task, new Set())
}
this.handlers.get(task)!.add(handler)
return () => {
this.handlers.get(task)?.delete(handler)
}
}
/**
* 移除所有监听
*/
static disposeAll() {
this.unlistenFn?.()
this.handlers.clear()
this.unlistenFn = null
this.initialized = false
this.initPromise = null
}
}
export default TaskEventManager