🚨 No longer maintained. Moved to @reallyland/node_mod. 🚨
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.
- Node.js >= 8.16.0
- NPM >= 6.4.1 (NPM comes with Node.js so there is no need to install separately.)
- perf_hooks (Added in
node@8.5.0
behind experimental flag)
# Install via NPM
$ npm install --save @reallyland/polling-observer
Performance API is strictly required before running any polling. To ensure performance.now
is available globally on Node.js, you can do:
/** Node.js */
import { performance } from 'perf_hooks';
global.performance = performance; // or globalThis.performance = performance;
interface DataType {
status: 'complete' | 'in-progress';
items: Record<string, any>[];
}
import { PollingObserver } from '@reallyland/polling-observer';
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;
});
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,
}
);
/**
* When polling finishes, it will either fulfill or reject depending on the status:
*
* | Status | Returns |
* | ------- | --------- |
* | finish | <value> |
* | timeout | <value> |
* | error | <reason> |
*
*/
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 */
};
const { PollingObserver } = require('@reallyland/polling-observer');
const obs = new PollingObserver((data/** entries, observer */) => {
const { status, items } = data || {};
const itemsLen = (items && items.length) || 0;
/** Stop polling when any of the conditions fulfills */
return 'complete' === status || itemsLen > 99;
});
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,
}
);
/**
* When polling finishes, it will either fulfill or reject depending on the status:
*
* | Status | Returns |
* | ------- | --------- |
* | finish | <value> |
* | timeout | <value> |
* | error | <reason> |
*
*/
obs.onfinish = (data, entries/**, 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 entries: `, entries.map(n => n.toJSON()));
/**
* [
* {
* duration: 100,
* entryType: 'polling-measure',
* name: 'polling:0',
* startTime: 100,
* },
* ...
* ]
*/
obs.disconnect(); /** Disconnect to clean up */
};
<!doctype html>
<html>
<head>
<script type="module">
import { PollingObserver } from 'https://unpkg.com/@reallyland/polling-observer@latest/dist/polling-observer.min.js';
// --snip
</script>
</head>
</html>
<!doctype html>
<html>
<head>
<script src="https://unpkg.com/@reallyland/polling-observer@latest/dist/polling-observer.umd.min.js"></script>
<script>
var { PollingObserver } = window.PollingObserver;
// --snip
</script>
</head>
</html>
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.
Note that no finish
event fires when the polling finishes. So observer.addEventListener('finish', ...)
will not work.
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.
When a polling fulfills, it returns an OnfinishFulfilled<T> object with status
set to finish
or timeout
and a value
in the type of T
.
When a polling rejects, it returns an OnfinishRejected object with status
set to error
and a reason
in the type of Error.
Status | Returns |
---|---|
finish |
<value> |
timeout |
<value> |
error |
<reason> |
Coming soon.
MIT License © Rong Sen Ng (motss)