version 1.1.0
This commit is contained in:
parent
617fdebee3
commit
1f46191482
13
README.md
13
README.md
|
@ -1,6 +1,6 @@
|
||||||
# Schedule Later
|
# Schedule Later
|
||||||
|
|
||||||
schedule-later is a static class that provides methods for managing date-time based tasks, such as starting timeouts and intervals at a specific time of day. Under-the-hood, it uses `setTimeout` and `setInterval`.
|
schedule-later provides methods for managing date-time based tasks, such as starting timeouts and intervals at a specific time of day. Under-the-hood, it uses `setTimeout` and `setInterval`.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ yarn add schedule-later
|
||||||
## Import
|
## Import
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Scheduler, TimeInMS } from 'schedule-later'
|
import { startTimeout, startInterval, TimeInMS } from 'schedule-later'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Key Concepts
|
## Key Concepts
|
||||||
|
@ -55,10 +55,7 @@ The `Scheduler` class provides two main static methods: `startTimeout` and `star
|
||||||
The `startTimeout` method starts a timeout that calls a given function after a specific delay. The delay is calculated based on the `TimeUntil` object passed to it. The method returns a `StopFunction` (see below).
|
The `startTimeout` method starts a timeout that calls a given function after a specific delay. The delay is calculated based on the `TimeUntil` object passed to it. The method returns a `StopFunction` (see below).
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
public static startTimeout(
|
function startTimeout(timerFunc: Function, start: TimeUntil): StopFunction
|
||||||
timerFunc: Function,
|
|
||||||
start: TimeUntil
|
|
||||||
): StopFunction;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### startInterval
|
### startInterval
|
||||||
|
@ -66,11 +63,11 @@ public static startTimeout(
|
||||||
The `startInterval` method starts an interval that calls a given function repeatedly with a fixed time delay between each call. Like `startTimeout`, the initial delay is calculated based on a `TimeUntil` object. The method returns a `StopFunction` (see below).
|
The `startInterval` method starts an interval that calls a given function repeatedly with a fixed time delay between each call. Like `startTimeout`, the initial delay is calculated based on a `TimeUntil` object. The method returns a `StopFunction` (see below).
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
public static startInterval(
|
function startInterval(
|
||||||
intervalFunc: Function,
|
intervalFunc: Function,
|
||||||
intervalMS: number,
|
intervalMS: number,
|
||||||
start?: TimeUntil
|
start?: TimeUntil
|
||||||
): StopFunction;
|
): StopFunction
|
||||||
```
|
```
|
||||||
|
|
||||||
## Stop Functions
|
## Stop Functions
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "schedule-later",
|
"name": "schedule-later",
|
||||||
"version": "1.0.6",
|
"version": "1.1.0",
|
||||||
"types": "dist/Scheduler.d.ts",
|
"types": "dist/Scheduler.d.ts",
|
||||||
"main": "dist/Scheduler.js",
|
"main": "dist/Scheduler.js",
|
||||||
"author": "Nightness",
|
"author": "Nightness",
|
||||||
|
@ -18,6 +18,6 @@
|
||||||
"@types/jest": "^29.5.3",
|
"@types/jest": "^29.5.3",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
"jest": "^29.6.1",
|
"jest": "^29.6.1",
|
||||||
"typescript": "^4.6.4"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { Scheduler } from './Scheduler'
|
import { startTimeout, startInterval, TimeInMS } from './Scheduler'
|
||||||
|
|
||||||
describe('Scheduler', () => {
|
describe('setTimeout Tests', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.useFakeTimers()
|
jest.useFakeTimers()
|
||||||
})
|
})
|
||||||
|
@ -9,10 +9,10 @@ describe('Scheduler', () => {
|
||||||
jest.clearAllTimers()
|
jest.clearAllTimers()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('startTimeout should correctly start and stop timeout', () => {
|
test('startTimeout, should start and stop properly', () => {
|
||||||
const callback = jest.fn()
|
const callback = jest.fn()
|
||||||
|
|
||||||
const stop = Scheduler.startTimeout(callback, { ms: 5000 })
|
const stop = startTimeout(callback, { ms: 5000 })
|
||||||
|
|
||||||
// Advance timers by less than the delay and check if callback has not been called
|
// Advance timers by less than the delay and check if callback has not been called
|
||||||
jest.advanceTimersByTime(2000)
|
jest.advanceTimersByTime(2000)
|
||||||
|
@ -26,10 +26,30 @@ describe('Scheduler', () => {
|
||||||
stop()
|
stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('startInterval should correctly start and stop interval', () => {
|
test('startTimeout, should not call the function if stop function is called', () => {
|
||||||
|
const fn = jest.fn()
|
||||||
|
const stop = startTimeout(fn, { ms: TimeInMS.SECOND })
|
||||||
|
|
||||||
|
expect(fn).not.toBeCalled()
|
||||||
|
stop()
|
||||||
|
jest.advanceTimersByTime(TimeInMS.SECOND)
|
||||||
|
expect(fn).not.toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('setInterval Tests', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('startInterval, should correctly start and repeat interval', () => {
|
||||||
const callback = jest.fn()
|
const callback = jest.fn()
|
||||||
|
|
||||||
const stop = Scheduler.startInterval(callback, 2000, { ms: 5000 })
|
const stop = startInterval(callback, 2000, { ms: 5000 })
|
||||||
|
|
||||||
// Advance timers by less than the initial delay and check if callback has not been called
|
// Advance timers by less than the initial delay and check if callback has not been called
|
||||||
jest.advanceTimersByTime(2000)
|
jest.advanceTimersByTime(2000)
|
||||||
|
@ -46,4 +66,14 @@ describe('Scheduler', () => {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
stop()
|
stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('startInterval, should not call the function if stop function is called', () => {
|
||||||
|
const fn = jest.fn()
|
||||||
|
const stop = startInterval(fn, 2000, { ms: TimeInMS.SECOND })
|
||||||
|
|
||||||
|
expect(fn).not.toBeCalled()
|
||||||
|
stop()
|
||||||
|
jest.advanceTimersByTime(TimeInMS.SECOND)
|
||||||
|
expect(fn).not.toBeCalled()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
288
src/Scheduler.ts
288
src/Scheduler.ts
|
@ -26,156 +26,154 @@ export type StopCancelFunction = (stopRunning: boolean) => void
|
||||||
|
|
||||||
export type StopFunction = (stopTime?: TimeUntil) => StopCancelFunction | null
|
export type StopFunction = (stopTime?: TimeUntil) => StopCancelFunction | null
|
||||||
|
|
||||||
export class Scheduler {
|
function timeUntil(start: TimeUntil) {
|
||||||
private static timeUntil(start: TimeUntil) {
|
if (start.ms) {
|
||||||
if (start.ms) {
|
return start.ms
|
||||||
return start.ms
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the current date
|
|
||||||
let now = new Date()
|
|
||||||
|
|
||||||
// set the target time
|
|
||||||
const targetTime = start.timeOfDay
|
|
||||||
? new Date(
|
|
||||||
now.getFullYear(),
|
|
||||||
now.getMonth(),
|
|
||||||
now.getDate(),
|
|
||||||
start.timeOfDay.hour,
|
|
||||||
start.timeOfDay.minute ?? 0,
|
|
||||||
start.timeOfDay.seconds ?? 0,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
: start.date ?? now
|
|
||||||
|
|
||||||
// if the target time has already passed today, set it for tomorrow
|
|
||||||
if (start.timeOfDay && now > targetTime) {
|
|
||||||
targetTime.setDate(targetTime.getDate() + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate the delay until the next target time
|
|
||||||
// @ts-ignore - TS doesn't know this is allowed
|
|
||||||
let delay = targetTime - now
|
|
||||||
|
|
||||||
return delay
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// get the current date
|
||||||
* Start a timeout
|
let now = new Date()
|
||||||
* @param {Function} timerFunc
|
|
||||||
* @param {TimeUntil} start
|
|
||||||
* @return {StopFunction}
|
|
||||||
*/
|
|
||||||
public static startTimeout(
|
|
||||||
timerFunc: Function,
|
|
||||||
start: TimeUntil
|
|
||||||
): StopFunction {
|
|
||||||
let delay = this.timeUntil(start)
|
|
||||||
|
|
||||||
// set a timeout to start the interval at the target time
|
// set the target time
|
||||||
let timeout: NodeJS.Timeout | null = null
|
const targetTime = start.timeOfDay
|
||||||
timeout = setTimeout(function () {
|
? new Date(
|
||||||
// Clear the timeout variable
|
now.getFullYear(),
|
||||||
timeout = null
|
now.getMonth(),
|
||||||
// call the function immediately
|
now.getDate(),
|
||||||
timerFunc()
|
start.timeOfDay.hour,
|
||||||
}, delay)
|
start.timeOfDay.minute ?? 0,
|
||||||
|
start.timeOfDay.seconds ?? 0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
: start.date ?? now
|
||||||
|
|
||||||
const stopNow = () => {
|
// if the target time has already passed today, set it for tomorrow
|
||||||
if (timeout) clearTimeout(timeout)
|
if (start.timeOfDay && now > targetTime) {
|
||||||
}
|
targetTime.setDate(targetTime.getDate() + 1)
|
||||||
|
|
||||||
// stop() function to stop the interval and timeout
|
|
||||||
// stop(stopInMS) will stop the interval and timeout in stopInMS milliseconds
|
|
||||||
// stop(stopHour, stopMinute) will stop the interval and timeout at the next stopHour:stopMinute
|
|
||||||
const stop = (stopTime?: TimeUntil): StopCancelFunction | null => {
|
|
||||||
if (stopTime === undefined) {
|
|
||||||
stopNow()
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((stopTime as any) instanceof Date) {
|
|
||||||
// @ts-ignore - TS doesn't know this is allowed
|
|
||||||
const timeFromNow = stopTime - new Date()
|
|
||||||
const stopTimeout = setTimeout(stopNow, timeFromNow)
|
|
||||||
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
|
||||||
return (stopRunning: boolean = false) => {
|
|
||||||
clearTimeout(stopTimeout)
|
|
||||||
if (stopRunning) stopNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopTimeout = setTimeout(stopNow, this.timeUntil(stopTime))
|
|
||||||
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
|
||||||
return (stopRunning: boolean = false) => {
|
|
||||||
clearTimeout(stopTimeout)
|
|
||||||
if (stopRunning) stopNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return a cleanup function
|
|
||||||
return stop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// calculate the delay until the next target time
|
||||||
* Start an interval
|
// @ts-ignore - TS doesn't know this is allowed
|
||||||
* @param {Function} intervalFunc
|
let delay = targetTime - now
|
||||||
* @param {number} intervalMS
|
|
||||||
* @param {TimeUntil} start
|
|
||||||
* @return {StopFunction}
|
|
||||||
*/
|
|
||||||
public static startInterval(
|
|
||||||
intervalFunc: Function,
|
|
||||||
intervalMS: number,
|
|
||||||
start?: TimeUntil
|
|
||||||
): StopFunction {
|
|
||||||
let delay = start ? this.timeUntil(start) : 0
|
|
||||||
|
|
||||||
// set a timeout to start the interval at the target time
|
return delay
|
||||||
let interval: number | null = null
|
}
|
||||||
let timeout: NodeJS.Timeout | null = setTimeout(function () {
|
|
||||||
// Clear the timeout variable
|
/**
|
||||||
timeout = null
|
* Start a timeout
|
||||||
// start the interval
|
* @param {Function} timerFunc
|
||||||
interval = setInterval(intervalFunc, intervalMS)
|
* @param {TimeUntil} start
|
||||||
// call the function immediately
|
* @return {StopFunction}
|
||||||
intervalFunc()
|
*/
|
||||||
}, delay)
|
export function startTimeout(
|
||||||
|
timerFunc: Function,
|
||||||
const stopNow = () => {
|
start: TimeUntil
|
||||||
if (timeout) clearTimeout(timeout)
|
): StopFunction {
|
||||||
if (interval) clearInterval(interval)
|
let delay = timeUntil(start)
|
||||||
}
|
|
||||||
|
// set a timeout to start the interval at the target time
|
||||||
// stop() function to stop the interval and timeout
|
let timeout: NodeJS.Timeout | null = null
|
||||||
// stop(stopInMS) will stop the interval and timeout in stopInMS milliseconds
|
timeout = setTimeout(function () {
|
||||||
// stop(stopHour, stopMinute) will stop the interval and timeout at the next stopHour:stopMinute
|
// Clear the timeout variable
|
||||||
const stop = (stopTime?: TimeUntil): StopCancelFunction | null => {
|
timeout = null
|
||||||
if (stopTime === undefined) {
|
// call the function immediately
|
||||||
stopNow()
|
timerFunc()
|
||||||
return null
|
}, delay)
|
||||||
}
|
|
||||||
|
const stopNow = () => {
|
||||||
if ((stopTime as any) instanceof Date) {
|
if (timeout) clearTimeout(timeout)
|
||||||
// @ts-ignore - TS doesn't know this is allowed
|
}
|
||||||
const timeFromNow = stopTime - new Date()
|
|
||||||
const stopTimeout = setTimeout(stopNow, timeFromNow)
|
// stop() function to stop the interval and timeout
|
||||||
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
// stop(stopInMS) will stop the interval and timeout in stopInMS milliseconds
|
||||||
return (stopRunning: boolean = false) => {
|
// stop(stopHour, stopMinute) will stop the interval and timeout at the next stopHour:stopMinute
|
||||||
clearTimeout(stopTimeout)
|
const stop = (stopTime?: TimeUntil): StopCancelFunction | null => {
|
||||||
if (stopRunning) stopNow()
|
if (stopTime === undefined) {
|
||||||
}
|
stopNow()
|
||||||
}
|
return null
|
||||||
|
}
|
||||||
const stopTimeout = setTimeout(stopNow, this.timeUntil(stopTime))
|
|
||||||
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
if ((stopTime as any) instanceof Date) {
|
||||||
return (stopRunning: boolean = false) => {
|
// @ts-ignore - TS doesn't know this is allowed
|
||||||
clearTimeout(stopTimeout)
|
const timeFromNow = stopTime - new Date()
|
||||||
if (stopRunning) stopNow()
|
const stopTimeout = setTimeout(stopNow, timeFromNow)
|
||||||
}
|
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
||||||
}
|
return (stopRunning: boolean = false) => {
|
||||||
|
clearTimeout(stopTimeout)
|
||||||
// return a cleanup function
|
if (stopRunning) stopNow()
|
||||||
return stop
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stopTimeout = setTimeout(stopNow, timeUntil(stopTime))
|
||||||
|
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
||||||
|
return (stopRunning: boolean = false) => {
|
||||||
|
clearTimeout(stopTimeout)
|
||||||
|
if (stopRunning) stopNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return a cleanup function
|
||||||
|
return stop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start an interval
|
||||||
|
* @param {Function} intervalFunc
|
||||||
|
* @param {number} intervalMS
|
||||||
|
* @param {TimeUntil} start
|
||||||
|
* @return {StopFunction}
|
||||||
|
*/
|
||||||
|
export function startInterval(
|
||||||
|
intervalFunc: Function,
|
||||||
|
intervalMS: number,
|
||||||
|
start?: TimeUntil
|
||||||
|
): StopFunction {
|
||||||
|
let delay = start ? timeUntil(start) : 0
|
||||||
|
|
||||||
|
// set a timeout to start the interval at the target time
|
||||||
|
let interval: number | null = null
|
||||||
|
let timeout: NodeJS.Timeout | null = setTimeout(function () {
|
||||||
|
// Clear the timeout variable
|
||||||
|
timeout = null
|
||||||
|
// start the interval
|
||||||
|
interval = setInterval(intervalFunc, intervalMS)
|
||||||
|
// call the function immediately
|
||||||
|
intervalFunc()
|
||||||
|
}, delay)
|
||||||
|
|
||||||
|
const stopNow = () => {
|
||||||
|
if (timeout) clearTimeout(timeout)
|
||||||
|
if (interval) clearInterval(interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop() function to stop the interval and timeout
|
||||||
|
// stop(stopInMS) will stop the interval and timeout in stopInMS milliseconds
|
||||||
|
// stop(stopHour, stopMinute) will stop the interval and timeout at the next stopHour:stopMinute
|
||||||
|
const stop = (stopTime?: TimeUntil): StopCancelFunction | null => {
|
||||||
|
if (stopTime === undefined) {
|
||||||
|
stopNow()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((stopTime as any) instanceof Date) {
|
||||||
|
// @ts-ignore - TS doesn't know this is allowed
|
||||||
|
const timeFromNow = stopTime - new Date()
|
||||||
|
const stopTimeout = setTimeout(stopNow, timeFromNow)
|
||||||
|
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
||||||
|
return (stopRunning: boolean = false) => {
|
||||||
|
clearTimeout(stopTimeout)
|
||||||
|
if (stopRunning) stopNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopTimeout = setTimeout(stopNow, timeUntil(stopTime))
|
||||||
|
// stopRunning is a boolean that will either cancel the stop (false), or stop the interval now (true)
|
||||||
|
return (stopRunning: boolean = false) => {
|
||||||
|
clearTimeout(stopTimeout)
|
||||||
|
if (stopRunning) stopNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return a cleanup function
|
||||||
|
return stop
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue