6 changed files with 78 additions and 16 deletions
@ -0,0 +1,56 @@ |
|||
import { ref, watchEffect, Ref } from 'vue'; |
|||
|
|||
/** |
|||
* Handle overlapping async evaluations |
|||
* |
|||
* @param cancelCallback The provided callback is invoked when a re-evaluation of the computed value is triggered before the previous one finished |
|||
*/ |
|||
export type AsyncComputedOnCancel = (cancelCallback: () => void) => void; |
|||
|
|||
/** |
|||
* A two-item tuple with the first item being a ref to the computed value and the second item holding a boolean ref, indicating whether the async computed value is currently (re-)evaluated |
|||
*/ |
|||
export type AsyncComputedResult<T> = [Ref<T>, Ref<boolean>]; |
|||
|
|||
/** |
|||
* Create an asynchronous computed dependency |
|||
* |
|||
* @param evaluationCallback The promise-returning callback which generates the computed value |
|||
* @param defaultValue A default value, used until the first evaluation finishes |
|||
*/ |
|||
export function asyncComputed<T>( |
|||
evaluationCallback: (onCancel: AsyncComputedOnCancel) => T | Promise<T>, |
|||
defaultValue?: T |
|||
): AsyncComputedResult<T> { |
|||
let counter = 0; |
|||
const current = ref(defaultValue) as Ref<T>; |
|||
const evaluating = ref<boolean>(false); |
|||
|
|||
watchEffect(async (onInvalidate: Fn) => { |
|||
counter++; |
|||
const counterAtBeginning = counter; |
|||
let hasFinished = false; |
|||
|
|||
try { |
|||
// Defer initial setting of `evaluating` ref
|
|||
// to avoid having it as a dependency
|
|||
Promise.resolve().then(() => { |
|||
evaluating.value = true; |
|||
}); |
|||
|
|||
const result = await evaluationCallback((cancelCallback) => { |
|||
onInvalidate(() => { |
|||
evaluating.value = false; |
|||
if (!hasFinished) cancelCallback(); |
|||
}); |
|||
}); |
|||
|
|||
if (counterAtBeginning === counter) current.value = result; |
|||
} finally { |
|||
evaluating.value = false; |
|||
hasFinished = true; |
|||
} |
|||
}); |
|||
|
|||
return [current, evaluating]; |
|||
} |
|||
Loading…
Reference in new issue