diff --git a/docs/.vuepress/components/timeoutExample.vue b/docs/.vuepress/components/timeoutExample.vue new file mode 100644 index 000000000..612ae0c0f --- /dev/null +++ b/docs/.vuepress/components/timeoutExample.vue @@ -0,0 +1,35 @@ + + diff --git a/docs/composable/web/timeout.md b/docs/composable/web/timeout.md new file mode 100644 index 000000000..03dad9c13 --- /dev/null +++ b/docs/composable/web/timeout.md @@ -0,0 +1,85 @@ +# Timeout + +> The [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout). + +## Parameters + +```js +import { useTimeout } from "vue-composable"; + +useTimeout(fn, delay); +``` + +| Parameters | Type | Required | Default | Description | +| ---------- | ---------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------- | +| fn | `Function` | `true` | | A function to be executed after the timer expires. | +| delay | `Number` | `false` | `0 ` | The time, in milliseconds (thousandths of a second), the timer should wait before the specified function is executed. | + +## State + +The `useTimeout` function exposes the following reactive state: + +```js +import { useTimeout } from "vue-composable"; + +const { ready } = useTimeout(fn, delay); +``` + +| State | Type | Description | +| ----- | ------------ | ----------- | ---------------------------------------------------------------------------------------------------- | +| ready | `Ref` | current timeout state:
 false - pending
 true - called
 null - canceled | + +## Methods + +The `useTimeout` function exposes the following methods: + +```js +import { useTimeout } from "vue-composable"; + +const { cancel } = useTimeout(fn, delay); +``` + +| Signature | Description | +| --------- | ------------------ | +| `cancel` | cancel the timeout | + +## Example + + + +### Code + +```vue + + +``` diff --git a/packages/vue-composable/__tests__/web/timeout.spec.ts b/packages/vue-composable/__tests__/web/timeout.spec.ts new file mode 100644 index 000000000..7ea1dac93 --- /dev/null +++ b/packages/vue-composable/__tests__/web/timeout.spec.ts @@ -0,0 +1,80 @@ +import { useTimeout } from "../../src"; +import { createVue } from "../utils"; +import { Ref, ref } from "../../src/api"; + +describe("timeout", () => { + jest.useFakeTimers(); + it("should be defined", () => { + expect(useTimeout).toBeDefined(); + }); + + it("should call passed function after given amount of time", async () => { + let count = 0; + useTimeout(() => { + count++; + }, 1000); + + expect(count).toBe(0); + jest.advanceTimersByTime(900); + // should not be resolved + expect(count).toBe(0); + + jest.advanceTimersByTime(100); + expect(count).toBe(1); + }); + + it("should set ready true after run callback", async () => { + const { ready } = useTimeout(() => {}, 1000); + + expect(ready.value).toBe(false); + jest.advanceTimersByTime(1000); + expect(ready.value).toBe(true); + }); + + it("should cancel function call when call cancel function", async () => { + let count = 0; + const { ready, cancel } = useTimeout(() => { + count++; + }, 1000); + + expect(ready.value).toBe(false); + expect(count).toBe(0); + + cancel(); + + jest.advanceTimersByTime(1000); + expect(ready.value).toBe(null); + expect(count).toBe(0); + }); + + it("should cancel on unMounted", async () => { + let ready: Ref = ref(false); + + const { mount, destroy } = createVue({ + template: `
`, + setup() { + ready = useTimeout(() => {}, 1000).ready; + }, + }); + + mount(); + + expect(ready.value).toBe(false); + jest.advanceTimersByTime(500); + expect(ready.value).toBe(false); + + destroy(); + expect(ready.value).toBe(null); + }); + + it("should default the delay to 0", () => { + let count = 0; + useTimeout(() => { + count++; + }); + + expect(count).toBe(0); + jest.runOnlyPendingTimers(); + expect(count).toBe(1); + }); +}); diff --git a/packages/vue-composable/src/web/index.ts b/packages/vue-composable/src/web/index.ts index 7147045aa..cf304be7c 100644 --- a/packages/vue-composable/src/web/index.ts +++ b/packages/vue-composable/src/web/index.ts @@ -11,4 +11,5 @@ export * from "./cssVariables"; export * from "./worker"; export * from "./share"; export * from "./clipboard"; +export * from "./timeout"; export { useWorkerFunction, WebWorkerFunctionOptions } from "./workerFunction"; diff --git a/packages/vue-composable/src/web/timeout.ts b/packages/vue-composable/src/web/timeout.ts new file mode 100644 index 000000000..358f337b2 --- /dev/null +++ b/packages/vue-composable/src/web/timeout.ts @@ -0,0 +1,43 @@ +import { ref, Ref, onUnmounted } from "../api"; + +interface UseTimeoutReturn { + /** + * current timeout state: + * false - pending + * true - called + * null - canceled + */ + ready: Ref; + /** + * cancel the timeout + */ + cancel: () => void; +} +/** + * @param fn setTimeout callback + * @param delay If this parameter is omitted, a value of 0 is used + * (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) + */ +export function useTimeout( + fn: () => void, + delay: number = 0 +): UseTimeoutReturn { + let ready: Ref = ref(false); + + const timeoutId = setTimeout(() => { + ready.value = true; + fn(); + }, delay); + + const cancel = () => { + ready.value = null; + clearTimeout(timeoutId); + }; + + onUnmounted(cancel); + + return { + ready, + cancel, + }; +}