Skip to content

Instantly share code, notes, and snippets.

@e7h4n
Created September 20, 2024 08:05
Show Gist options
  • Save e7h4n/09ab3ebf89e2a8c432e67b54844273c3 to your computer and use it in GitHub Desktop.
Save e7h4n/09ab3ebf89e2a8c432e67b54844273c3 to your computer and use it in GitHub Desktop.
mobx + abortSignal 的一点点例子
// 这个测试里我们用 mobx 来建立一个应用程序
// 这个程序中,当 dialog 的 show 状态变为 true 时,我们开启一个定时器
// 这个定时器每秒给一个全局自增 counter+1
// 当 dialog 的状态变为 false 时,我们重置这个全局定时器
// 在这个例子中,所有的清理工作都应该用 transaction / abortSignal 来完成,而不是调用方主动重置
import { afterEach, beforeEach, expect, test, vi } from "vitest";
import { makeAutoObservable, reaction } from "mobx"
import { interval } from "signal-timers";
import { transaction } from "signal-transaction";
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
test('简单的 setter + 复杂的响应式计算', async () => {
class Store {
dialog = {
show: false
}
counter = 0
timer: ReturnType<typeof setInterval> | null = null
abortTimer?: () => void
constructor() {
makeAutoObservable(this)
reaction(
() => this.dialog.show,
(show) => {
if (show) {
const controller = new AbortController()
const signal = controller.signal;
this.timer = setInterval(() => {
if (!signal.aborted) {
this.counter++
}
}, 1000)
signal.addEventListener('abort', () => {
if (this.timer) {
clearInterval(this.timer)
}
this.counter = 0
})
this.abortTimer = () => { controller.abort() }
} else {
this.abortTimer?.()
}
}
)
}
showDialog() {
this.dialog.show = true
}
hideDialog() {
this.dialog.show = false
}
}
const store = new Store()
store.showDialog()
await vi.advanceTimersByTimeAsync(3000)
expect(store.counter).toBe(3)
store.hideDialog()
expect(store.counter).toBe(0)
})
test('复杂的 setter', async () => {
class Store {
dialog = {
show: false
}
counter = 0
private dialogController?: AbortController
showDialog(signal: AbortSignal) {
this.dialogController?.abort()
this.dialogController = new AbortController()
const dialogSignal = AbortSignal.any([this.dialogController.signal, signal])
const { act } = transaction(dialogSignal)
act(() => {
this.dialog.show = true
return () => {
this.dialog.show = false
this.counter = 0
}
})
interval(() => {
act(() => {
this.counter += 1
})
}, 1000, { signal: dialogSignal })
}
hideDialog() {
this.dialogController?.abort()
}
}
const rootController = new AbortController()
const store = new Store()
// 重复 show 也不会有多的定时器
store.showDialog(rootController.signal)
store.showDialog(rootController.signal)
store.showDialog(rootController.signal)
await vi.advanceTimersByTimeAsync(3000)
expect(store.counter).toBe(3)
store.showDialog(rootController.signal)
expect(store.counter).toBe(0)
await vi.advanceTimersByTimeAsync(3000)
expect(store.counter).toBe(3)
store.hideDialog()
expect(store.counter).toBe(0)
expect(store.dialog.show).toBeFalsy()
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment