Skip to content

Instantly share code, notes, and snippets.

@adenvt
Created March 7, 2024 02:17
Show Gist options
  • Save adenvt/702a51d501f081eb994b764436fd7879 to your computer and use it in GitHub Desktop.
Save adenvt/702a51d501f081eb994b764436fd7879 to your computer and use it in GitHub Desktop.
Awaitable variable change JS
type OnChange<T> = (value: T) => void
type OffChange = () => void
interface Ref <T> {
value: T,
change: (fn: OnChange<T>) => OffChange,
toBe: (expect: T) => Promise<void>,
}
/**
* Reactive variable
*
* @param initialValue initial value
* @example
*
* const isBusy = useRef(false)
*
* function onClick () {
* // Wait other tobe done
* if (isBusy.value)
* await isBusy.toBe(false)
*
* isBusy.value = true
*
* // Heavy async function
* setTimeout(() => {
* isBusy.value = false
* },5000)
* }
*/
export function useRef<T = any> (): Ref<T | undefined>
export function useRef<T = any> (initialValue: T): Ref<T>
export function useRef<T> (initialValue?: T): Ref<T | undefined> {
let value: T | undefined = initialValue
const watcher = new Set<OnChange<T>>()
return {
/**
* Get value
*/
get value (): T | undefined {
return value
},
/**
* Set value
*/
set value (newValue: T) {
value = newValue
// emit on-change
for (const emit of watcher)
emit(newValue)
},
/**
* Add on change listener
* @param fn on change handler
*/
change (fn: OnChange<T>): OffChange {
watcher.add(fn)
return () => {
watcher.delete(fn)
}
},
/**
* Wait until
* @param expect expected value
*/
toBe (expect: T | undefined) {
return new Promise<void>((resolve) => {
const stop = this.change((value) => {
if (value === expect) {
stop()
resolve()
}
})
})
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment