@hardlydifficult/poller 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -96
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @hardlydifficult/poller
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lightweight polling utility with debounced triggers, overlapping request handling, and deep equality change detection.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -13,132 +13,88 @@ npm install @hardlydifficult/poller
|
|
|
13
13
|
```typescript
|
|
14
14
|
import { Poller } from "@hardlydifficult/poller";
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
return res.json();
|
|
16
|
+
const fetchFn = async () => {
|
|
17
|
+
const response = await fetch("https://api.example.com/status");
|
|
18
|
+
return response.json();
|
|
20
19
|
};
|
|
21
20
|
|
|
22
|
-
// Create and start the poller
|
|
23
21
|
const poller = new Poller(
|
|
24
|
-
|
|
25
|
-
(
|
|
26
|
-
|
|
27
|
-
},
|
|
28
|
-
5000 // Poll every 5 seconds
|
|
22
|
+
fetchFn,
|
|
23
|
+
(data, prev) => console.log("Data changed:", data),
|
|
24
|
+
5000 // 5-second interval
|
|
29
25
|
);
|
|
30
26
|
|
|
31
27
|
await poller.start();
|
|
32
|
-
// Polls every
|
|
33
|
-
|
|
34
|
-
// Manually trigger a debounced poll (e.g., after user action)
|
|
35
|
-
poller.trigger(1000); // Waits 1s, then polls once
|
|
28
|
+
// Polls every 5 seconds and logs only when data changes
|
|
36
29
|
|
|
37
|
-
//
|
|
30
|
+
// Later, stop polling
|
|
38
31
|
poller.stop();
|
|
39
32
|
```
|
|
40
33
|
|
|
41
|
-
## Polling
|
|
34
|
+
## Polling with Change Detection
|
|
35
|
+
|
|
36
|
+
The `Poller` class polls a fetch function periodically and invokes a callback only when the result changes. Change detection uses deep equality via `JSON.stringify` comparison, ensuring that structurally identical values (even with different object references) do not trigger redundant callbacks.
|
|
37
|
+
|
|
38
|
+
### Constructor Parameters
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
| Parameter | Type | Description |
|
|
41
|
+
|-------------|-------------------------------------|-----------------------------------------------------|
|
|
42
|
+
| `fetchFn` | `() => Promise<T>` | Async function that returns the data to poll |
|
|
43
|
+
| `onChange` | `(current: T, previous: T | undefined) => void` | Callback invoked when data changes |
|
|
44
|
+
| `intervalMs`| `number` | Polling interval in milliseconds |
|
|
45
|
+
| `onError?` | `(error: unknown) => void` (optional) | Optional error handler for fetch failures |
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
- `stop()`: Cancels timers and clears any pending debounced trigger. Safe to call multiple times.
|
|
47
|
+
### Poller API
|
|
47
48
|
|
|
48
49
|
```typescript
|
|
50
|
+
// Start polling (idempotent — safe to call multiple times)
|
|
49
51
|
await poller.start();
|
|
50
|
-
await poller.start(); // No-op
|
|
51
52
|
|
|
53
|
+
// Stop polling and clear timers
|
|
52
54
|
poller.stop();
|
|
53
|
-
poller.stop(); // No-op
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Change Detection
|
|
57
|
-
|
|
58
|
-
### Deep Equality via JSON
|
|
59
|
-
|
|
60
|
-
Change detection uses `JSON.stringify()` comparison, enabling structural equality checks for objects and arrays—even when references differ.
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const onChange = (current, previous) => {
|
|
65
|
-
// Fires only when structure changes
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const poller = new Poller(fetchFn, onChange, 1000);
|
|
69
|
-
// Even if new object reference returned, onChange won’t fire unless JSON differs
|
|
56
|
+
// Manually trigger a poll (debounced by default)
|
|
57
|
+
poller.trigger(1000); // Debounced 1s (default 1000ms)
|
|
70
58
|
```
|
|
71
59
|
|
|
72
|
-
###
|
|
60
|
+
### Debounced Manual Trigger
|
|
61
|
+
|
|
62
|
+
The `trigger()` method allows manually forcing a poll while debouncing multiple rapid calls:
|
|
73
63
|
|
|
74
64
|
```typescript
|
|
75
|
-
|
|
65
|
+
await poller.start();
|
|
66
|
+
poller.trigger(500); // Schedules a poll after 500ms
|
|
67
|
+
poller.trigger(500); // Resets debounce — only one poll fires
|
|
76
68
|
```
|
|
77
69
|
|
|
78
|
-
|
|
79
|
-
- `previous`: Prior value, or `undefined` on first poll
|
|
80
|
-
|
|
81
|
-
## Error Handling
|
|
70
|
+
### Error Handling
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Provide an `onError` handler to manage fetch failures; polling continues regardless.
|
|
72
|
+
Errors during polling do not halt the poller. They are optionally reported via `onError`, if provided.
|
|
86
73
|
|
|
87
74
|
```typescript
|
|
88
75
|
const poller = new Poller(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
(
|
|
93
|
-
|
|
94
|
-
|
|
76
|
+
async () => {
|
|
77
|
+
throw new Error("Network failure");
|
|
78
|
+
},
|
|
79
|
+
(data) => console.log(data),
|
|
80
|
+
2000,
|
|
81
|
+
(error) => console.error("Poll failed:", error)
|
|
95
82
|
);
|
|
83
|
+
await poller.start();
|
|
84
|
+
// Logs: Poll failed: Error: Network failure
|
|
85
|
+
// Continues polling after each error
|
|
96
86
|
```
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
- If `onError` is omitted, errors are silently suppressed.
|
|
100
|
-
|
|
101
|
-
## Manual Triggering
|
|
88
|
+
## Overlapping Request Handling
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- `trigger(debounceMs?: number)`: Schedules a one-time poll after a debounce delay (default: 1000ms).
|
|
106
|
-
- Multiple rapid calls cancel previous timeouts—only the last one fires.
|
|
90
|
+
The `Poller` skips new polls while a fetch is still in progress, preventing overlapping requests.
|
|
107
91
|
|
|
108
92
|
```typescript
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
- Concurrent fetches are skipped—only one in-flight request is allowed at a time.
|
|
119
|
-
- Prevents resource waste and race conditions during slow network calls.
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
// Interval fires every 5s; if fetch takes 6s:
|
|
123
|
-
// - Second interval fire is skipped
|
|
124
|
-
// - Third interval fire executes after first completes
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## API Reference
|
|
128
|
-
|
|
129
|
-
### `Poller<T>`
|
|
130
|
-
|
|
131
|
-
| Parameter | Type | Description |
|
|
132
|
-
|-----------|------|-------------|
|
|
133
|
-
| `fetchFn` | `() => Promise<T>` | Async function to fetch data |
|
|
134
|
-
| `onChange` | `(current: T, previous: T \| undefined) => void` | Callback on value change |
|
|
135
|
-
| `intervalMs` | `number` | Polling interval in milliseconds |
|
|
136
|
-
| `onError?` | `(error: unknown) => void` | Optional error handler |
|
|
137
|
-
|
|
138
|
-
### Methods
|
|
139
|
-
|
|
140
|
-
| Method | Signature | Description |
|
|
141
|
-
|--------|-----------|-------------|
|
|
142
|
-
| `start` | `(): Promise<void>` | Begin polling immediately and then at `intervalMs` |
|
|
143
|
-
| `stop` | `(): void` | Cancel all timers and pending triggers |
|
|
144
|
-
| `trigger` | `(debounceMs?: number) => void` | Trigger a one-time poll after debounce delay |
|
|
93
|
+
const fetchFn = vi.fn().mockImplementation(() => {
|
|
94
|
+
// Simulates slow network — never resolves before interval
|
|
95
|
+
return new Promise((resolve) => setTimeout(() => resolve("data"), 6000));
|
|
96
|
+
});
|
|
97
|
+
const poller = new Poller(fetchFn, () => {}, 1000);
|
|
98
|
+
await poller.start();
|
|
99
|
+
// Only one fetch runs at a time — subsequent intervals are skipped until it completes
|
|
100
|
+
```
|