Like PerformanceObserver or any other observer APIs you could find in a browser, but this is for polling. Not only does it run polling with defined parameters but also collect polling metrics for each run until timeout or a defined condition fulfills.
This relies on Performance API which is available globally by default. For Node.js users, the API has been implemented and shipped as of Node.js v8.5.0. But it is not one of the global APIs, do the following before using the module:
const { performance } = require('perf_hooks');
global.performance = performance;
interface DataType {
status: 'complete' | 'in-progress';
items: Record<string, any>[];
}
import { PollingObserver } from 'nodemod/dist/polling-observer/index.js';
const obs = new PollingObserver((data/** list, observer */) => {
const { status, items } = data || {};
const itemsLen = (items && items.length) || 0;
/** Stop polling when any of the conditions fulfills */
return 'complete' === status || itemsLen > 99;
});
/**
* When polling finishes, it will either fulfill or reject depending on the status:
*
* | Status | Returns |
* | ------- | --------- |
* | finish | <value> |
* | timeout | <value> |
* | error | <reason> |
*
* Alternatively, `obs.addEventListener('finish', ...);` works too.
*/
obs.onfinish = (data, records/**, observer */) => {
const { status, value, reason } = data || {};
switch (status) {
case 'error': {
console.error(`Polling fails due to: `, reason);
break;
}
case 'timeout': {
console.log(`Polling timeouts after 30 seconds: `, value);
break;
}
case 'finish':
default: {
console.log(`Polling finishes: `, value);
}
}
console.log(`Formatted polling records: `, records.map(n => n.toJSON()));
/**
* [
* {
* duration: 100,
* entryType: 'polling-measure',
* name: 'polling:0',
* startTime: 100,
* },
* ...
* ]
*/
obs.disconnect(); /** Disconnect to clean up */
};
obs.observe(
async () => {
/** Polling callback - fetch resources */
const r = await fetch('https://example.com/api?key=123');
const d = await r.json();
return d;
},
/** Run polling (at least) every 2 seconds and timeout if it exceeds 30 seconds */
{
interval: 2e3,
timeout: 30e3,
}
);
interface OnfinishFulfilled<T> {
status: 'finish' | 'timeout';
value: T;
}
interface OnfinishRejected {
status: 'error';
reason: Error;
}
interface PollingMeasure {
duration: number;
entryType: 'polling-measure';
name: string;
startTime: number;
}
duration
<number> Duration of the polling takes in milliseconds.entryType
<string> Entry type, defaults topolling-measure
.name
<string> Polling name in the format ofpolling:<index>
where<index>
starts from0
and increments on each polling.startTime
<string> Relative timestamp (in milliseconds ) indicates when the polling starts at.
- <Function> Returns a JSON representation of the polling object's properties.
conditionCallback
<Function> Condition callback to be executed in each polling and return the condition result in the type of boolean, e.g. returntrue
to stop next poll.data
<T
> Polling data returned bycallback
in the type ofT
which defined in the PollingObserver.observe() method.entries
<Array<PollingMeasure>> A list of PollingMeasure objects.observer
<PollingObserver<T
>> Created PollingObserver object.- returns: <boolean> If
true
, the polling stops. Returningfalse
will result in an infinite polling as the condition will never meet.
- returns: <PollingObserver<
T
>> PollingObserver object.
The method is used to initiate polling with a polling callback and optional configuration.
callback
<Function> Callback to be executed in each polling and return the result so that it will be passed as the first argument inconditionCallback
.- returns: <
T
| Promise<T
>> Return polling result in the type ofT
orPromise<T>
in each polling.
- returns: <
options
<Object> Optional configuration to run the polling.interval
<number> Optional interval in milliseconds. This is the minimum delay before starting the next polling.timeout
<number> Optional timeout in milliseconds. Polling ends when it reaches the defined timeout even though the condition has not been met yet. As long astimeout
is not a number or it has a value that is less than 1, it indicates an infinite polling. The polling needs to be stopped manually by calling PollingObserver.disconnect() method.
Once a PollingObserver
disconnects, the polling stops and all polling metrics will be cleared. Calling PollingObserver.takeRecords() after the disconnection will always return an empty record.
A onfinish
event handler can be used to retrieve polling records after a disconnection but it has to be attached before disconnecting the observer.
The method returns a list of PollingMeasure object containing the metrics of each polling.
- returns: <Array<PollingMeasure>> A list of PollingMeasure objects.
Alternatively, an event handler can be setup to listen for the finish
event. See finish.
Event handler for when a polling finishes. When a polling finishes, it can either be fulfilled with a value
or rejected with a reason
. Any one of which contains a status
field to tell the state of the finished polling.
onfinishCallback
<Function> Callback to be executed when a polling finishes.-
data
<OnfinishFulfilled<T>|OnfinishRejected> When a polling fulfills, it returns an OnfinishFulfilled<T> object withstatus
set tofinish
ortimeout
and avalue
in the type ofT
. Whereas a polling rejects, it returns an OnfinishRejected object withstatus
set toerror
and areason
in the type of Error.Status Returns finish
<value> timeout
<value> error
<reason> -
entries
<Array<PollingMeasure>> A list of PollingMeasure objects. -
observer
<PollingObserver<T
>> Created PollingObserver object.
-
finish
event fires when a polling finishes.
const obs = new PollingObserver(/** --snip */);
// --snip
/** Same as using obs.onfinish = ... */
obs.addEventListener('finish', (ev: CustomEvent) => {
const {
detail: [
{ status, value/**, reason */ },
records,
observer,
],
} = ev;
// --snip
});
MIT License © Rong Sen Ng