From 94ce3ed59b8af64934718e40197ece03df9c4ae7 Mon Sep 17 00:00:00 2001 From: psxcode Date: Fri, 10 Apr 2020 17:42:30 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=A3=20tymeout:=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + packages/tymeout/LICENSE | 22 +++ packages/tymeout/package.json | 27 ++++ packages/tymeout/src/index.ts | 5 + packages/tymeout/src/ping.ts | 25 ++++ packages/tymeout/src/types.ts | 3 + packages/tymeout/src/wait-promise.ts | 8 ++ packages/tymeout/src/wait-time-promise.ts | 3 + packages/tymeout/src/wait-time.ts | 3 + packages/tymeout/src/wait.ts | 14 ++ packages/tymeout/test/ping.ts | 71 ++++++++++ packages/tymeout/test/wait-promise.ts | 86 ++++++++++++ packages/tymeout/test/wait.ts | 161 ++++++++++++++++++++++ yarn.lock | 5 + 14 files changed, 434 insertions(+) create mode 100644 packages/tymeout/LICENSE create mode 100644 packages/tymeout/package.json create mode 100644 packages/tymeout/src/index.ts create mode 100644 packages/tymeout/src/ping.ts create mode 100644 packages/tymeout/src/types.ts create mode 100644 packages/tymeout/src/wait-promise.ts create mode 100644 packages/tymeout/src/wait-time-promise.ts create mode 100644 packages/tymeout/src/wait-time.ts create mode 100644 packages/tymeout/src/wait.ts create mode 100644 packages/tymeout/test/ping.ts create mode 100644 packages/tymeout/test/wait-promise.ts create mode 100644 packages/tymeout/test/wait.ts diff --git a/package.json b/package.json index e3a83bc17..aa1011132 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "packages/stroki", "packages/syntx", "packages/tsfn", + "packages/tymeout", "packages/typeon", "packages/unchunk", "packages/weslint", diff --git a/packages/tymeout/LICENSE b/packages/tymeout/LICENSE new file mode 100644 index 000000000..ab7955490 --- /dev/null +++ b/packages/tymeout/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2018 Alex Feinstein +Copyright (c) 2020 NexTools + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/tymeout/package.json b/packages/tymeout/package.json new file mode 100644 index 000000000..d28c2b1be --- /dev/null +++ b/packages/tymeout/package.json @@ -0,0 +1,27 @@ +{ + "name": "tymeout", + "version": "0.0.0", + "description": "Functional setTimeout and setInterval", + "keywords": [ + "setTimeout", + "setInterval", + "promise", + "timeout", + "functional" + ], + "engines": { + "node": ">=10.13.0" + }, + "sideEffects": false, + "main": "src/index.ts", + "private": false, + "license": "MIT", + "author": "psxcode (https://github.com/psxcode)", + "repository": "nextools/tymeout", + "devDependencies": { + "@types/tape": "^4.2.34", + "spyfn": "^1.0.0", + "tape": "^5.0.0-next.5", + "time-test": "^0.1.0" + } +} diff --git a/packages/tymeout/src/index.ts b/packages/tymeout/src/index.ts new file mode 100644 index 000000000..dadff2981 --- /dev/null +++ b/packages/tymeout/src/index.ts @@ -0,0 +1,5 @@ +export { wait } from './wait' +export { waitPromise } from './wait-promise' +export { waitTime } from './wait-time' +export { waitTimePromise } from './wait-time-promise' +export { ping } from './ping' diff --git a/packages/tymeout/src/ping.ts b/packages/tymeout/src/ping.ts new file mode 100644 index 000000000..c8b3dc940 --- /dev/null +++ b/packages/tymeout/src/ping.ts @@ -0,0 +1,25 @@ +import { waitFactory } from './wait' +import { ClearTimeoutFn, SetTimeoutFn } from './types' + +export const pingFactory = (setTimeout: SetTimeoutFn, clearTimeout: ClearTimeoutFn) => { + const waitFn = waitFactory(setTimeout, clearTimeout) + + return (timeGetter: () => number) => + (cb: () => void) => { + let unsub: () => void + const wait = waitFn(timeGetter)(() => { + cb() + unsub = wait() + }) + + return () => { + unsub = wait() + + return () => { + unsub() + } + } + } +} + +export const ping = pingFactory(setTimeout, clearTimeout) diff --git a/packages/tymeout/src/types.ts b/packages/tymeout/src/types.ts new file mode 100644 index 000000000..521a1b1a6 --- /dev/null +++ b/packages/tymeout/src/types.ts @@ -0,0 +1,3 @@ +export type TimeoutId = any +export type SetTimeoutFn = (cb: () => void, ms: number) => TimeoutId +export type ClearTimeoutFn = (id: TimeoutId) => void diff --git a/packages/tymeout/src/wait-promise.ts b/packages/tymeout/src/wait-promise.ts new file mode 100644 index 000000000..5fac5baf4 --- /dev/null +++ b/packages/tymeout/src/wait-promise.ts @@ -0,0 +1,8 @@ +import { SetTimeoutFn } from './types' + +export const waitPromiseFactory = (setTimeout: SetTimeoutFn) => + (timeGetter = () => 0) => + (ms = timeGetter()): Promise => + new Promise((resolve) => setTimeout(() => resolve(), ms)) + +export const waitPromise = waitPromiseFactory(setTimeout) diff --git a/packages/tymeout/src/wait-time-promise.ts b/packages/tymeout/src/wait-time-promise.ts new file mode 100644 index 000000000..9107d769a --- /dev/null +++ b/packages/tymeout/src/wait-time-promise.ts @@ -0,0 +1,3 @@ +import { waitPromise } from './wait-promise' + +export const waitTimePromise = waitPromise() diff --git a/packages/tymeout/src/wait-time.ts b/packages/tymeout/src/wait-time.ts new file mode 100644 index 000000000..4155b4eb8 --- /dev/null +++ b/packages/tymeout/src/wait-time.ts @@ -0,0 +1,3 @@ +import { wait } from './wait' + +export const waitTime = wait() diff --git a/packages/tymeout/src/wait.ts b/packages/tymeout/src/wait.ts new file mode 100644 index 000000000..7807d22c9 --- /dev/null +++ b/packages/tymeout/src/wait.ts @@ -0,0 +1,14 @@ +import { ClearTimeoutFn, SetTimeoutFn } from './types' + +export const waitFactory = (setTimeout: SetTimeoutFn, clearTimeout: ClearTimeoutFn) => + (timeGetter = () => 0) => + (cb: () => void) => + (ms = timeGetter()) => { + const id = setTimeout(cb, ms) + + return () => { + clearTimeout(id) + } + } + +export const wait = waitFactory(setTimeout, clearTimeout) diff --git a/packages/tymeout/test/ping.ts b/packages/tymeout/test/ping.ts new file mode 100644 index 000000000..b700fb5c1 --- /dev/null +++ b/packages/tymeout/test/ping.ts @@ -0,0 +1,71 @@ +/* eslint-disable no-param-reassign */ +import test from 'tape' +import { createSpy, getSpyCalls } from 'spyfn' +import { tickTimeout, makeSetTimeoutContext, makeSetTimeout, makeClearTimeout, getSetTimeoutCalls, getClearTimeoutCalls } from 'time-test' +import { pingFactory } from '../src/ping' + +test('ping', (t) => { + const timeoutGetter = createSpy(((i = 0, v = [100, 200, 300]) => () => v[i++])()) + const cb = createSpy(() => {}) + + const timeContext = makeSetTimeoutContext() + const tick = tickTimeout(timeContext) + + const pinger = pingFactory( + makeSetTimeout(timeContext), + makeClearTimeout(timeContext) + )(timeoutGetter)(cb) + + // Begin pinging + const unsub = pinger() + + t.deepEquals( + getSetTimeoutCalls(timeContext), + [ + { delay: 100 }, + ], + 'should call setInterval' + ) + + tick(50) + + t.deepEquals( + getSpyCalls(cb), + [], + 'should not call spy' + ) + + tick() + tick() + unsub() + tick() + + t.deepEquals( + getSetTimeoutCalls(timeContext), + [ + { delay: 100 }, + { delay: 200 }, + { delay: 300 }, + ], + 'should call setTimeout' + ) + + t.deepEquals( + getClearTimeoutCalls(timeContext), + [ + { id: 2 }, + ], + 'should call clearTimeout' + ) + + t.deepEquals( + getSpyCalls(cb), + [ + [], + [], + ], + 'should call spy' + ) + + t.end() +}) diff --git a/packages/tymeout/test/wait-promise.ts b/packages/tymeout/test/wait-promise.ts new file mode 100644 index 000000000..fef5f3539 --- /dev/null +++ b/packages/tymeout/test/wait-promise.ts @@ -0,0 +1,86 @@ +/* eslint-disable no-param-reassign */ +import test from 'tape' +import { createSpy } from 'spyfn' +import { makeSetTimeoutContext, tickTimeout, makeSetTimeout, getSetTimeoutCalls, getClearTimeoutCalls } from 'time-test' +import { waitPromiseFactory } from '../src/wait-promise' + +test('waitPromise', async (t) => { + const timeoutGetter = createSpy(((i = 0, v = [100, 200, 300]) => () => v[i++])()) + + const timeContext = makeSetTimeoutContext() + const tick = tickTimeout(timeContext) + + const waiter = waitPromiseFactory( + makeSetTimeout(timeContext) + )(timeoutGetter) + + // Use getter value + const p1 = waiter() + // Use explicit value + const p2 = waiter(500) + + t.deepEquals( + getSetTimeoutCalls(timeContext), + [ + { delay: 100 }, + { delay: 500 }, + ], + 'should call setTimeout' + ) + + t.deepEquals( + getClearTimeoutCalls(timeContext), + [], + 'should not call clearTimeout' + ) + + tick() + + // Promise should be now resolved + await p1 + + tick() + + // Promise should be now resolved + await p2 +}) + +test('waitPromise; no getter', async (t) => { + const timeContext = makeSetTimeoutContext() + const tick = tickTimeout(timeContext) + + const waiter = waitPromiseFactory( + makeSetTimeout(timeContext) + )() + + // Use getter value + const p1 = waiter() + // Use explicit value + const p2 = waiter(500) + + t.deepEquals( + getSetTimeoutCalls(timeContext), + [ + { delay: 0 }, + { delay: 500 }, + ], + 'should call setTimeout' + ) + + t.deepEquals( + getClearTimeoutCalls(timeContext), + [], + 'should not call clearTimeout' + ) + + tick() + + // Promise should be now resolved + await p1 + + tick() + + // Promise should be now resolved + await p2 +}) + diff --git a/packages/tymeout/test/wait.ts b/packages/tymeout/test/wait.ts new file mode 100644 index 000000000..857498a52 --- /dev/null +++ b/packages/tymeout/test/wait.ts @@ -0,0 +1,161 @@ +/* eslint-disable no-param-reassign */ +import test from 'tape' +import { makeSetTimeoutContext, makeSetTimeout, makeClearTimeout, tickTimeout, getSetTimeoutCalls, getClearTimeoutCalls } from 'time-test' +import { createSpy, getSpyCalls } from 'spyfn' +import { waitFactory } from '../src/wait' + +test('wait', (t) => { + const timeoutGetter = createSpy(((i = 0, v = [100, 200, 300]) => () => v[i++])()) + const cb = createSpy(() => {}) + + const timeContext = makeSetTimeoutContext() + const tick = tickTimeout(timeContext) + + const waiter = waitFactory( + makeSetTimeout(timeContext), + makeClearTimeout(timeContext) + )(timeoutGetter)(cb) + + // Run waiter using getter + waiter() + + // Get the unsubscribe function + const unsub = waiter() + + // Override getter value + waiter(500) + + t.deepEquals( + getSetTimeoutCalls(timeContext), + [ + { delay: 100 }, + { delay: 200 }, + { delay: 500 }, + ], + 'should call setTimeout' + ) + + t.deepEquals( + getClearTimeoutCalls(timeContext), + [], + 'should not call clearTimeout' + ) + + // Tick timeout + tick(50) + + t.deepEquals( + getSpyCalls(cb), + [], + 'should not invoke callback' + ) + + // Add 100ms more + tick(100) + + t.deepEquals( + getSpyCalls(cb), + [ + [], + ], + 'should invoke callback' + ) + + // Unsubscribe + unsub() + + // more time + tick(1000) + + t.deepEquals( + getSpyCalls(cb), + [ + [], + [], + ], + 'should invoke callback' + ) + + t.deepEquals( + getClearTimeoutCalls(timeContext), + [ + { id: 1 }, + ], + 'should call clearTimeout' + ) + + t.end() +}) + +test('wait: no getter', (t) => { + const cb = createSpy(() => {}) + + const timeContext = makeSetTimeoutContext() + const tick = tickTimeout(timeContext) + + const waiter = waitFactory( + makeSetTimeout(timeContext), + makeClearTimeout(timeContext) + )()(cb) + + // Use default value + waiter() + + // Get the unsubscribe function + const unsub = waiter(100) + + // Use explicit time + waiter(500) + + t.deepEquals( + getSetTimeoutCalls(timeContext), + [ + { delay: 0 }, + { delay: 100 }, + { delay: 500 }, + ], + 'should call setTimeout' + ) + + t.deepEquals( + getClearTimeoutCalls(timeContext), + [], + 'should not call clearTimeout' + ) + + // Tick timeout + tick(50) + + t.deepEquals( + getSpyCalls(cb), + [ + [], + ], + 'should invoke callback' + ) + + // Unsubscribe + unsub() + + // more time + tick(1000) + + t.deepEquals( + getSpyCalls(cb), + [ + [], + [], + ], + 'should invoke callback' + ) + + t.deepEquals( + getClearTimeoutCalls(timeContext), + [ + { id: 1 }, + ], + 'should call clearTimeout' + ) + + t.end() +}) diff --git a/yarn.lock b/yarn.lock index 29168a9e6..78592ea0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13090,6 +13090,11 @@ time-stamp@^1.0.0: resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= +time-test@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/time-test/-/time-test-0.1.0.tgz#cc1c1df0cb7854989981531ed65961c8faa54357" + integrity sha512-Ri9sQxMxhcJ+u8SzgN1oao1AR68JX0UQtyjdR7vMPGNXroYn5OgKc6VnYvoP2/F1Lhco9EYe1bh6DWoxAkseoQ== + timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"