UNPKG

16.6 kBMarkdownView Raw
1<div align="center" style="text-align: center;">
2 <h1 style="border-bottom: none;">@reallyland/polling-observer</h1>
3
4 <p>A new way of running polling function with observer pattern</p>
5</div>
6
7<hr />
8
9<a href="https://www.buymeacoffee.com/RLmMhgXFb" target="_blank" rel="noopener noreferrer"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 20px !important;width: auto !important;" ></a>
10[![tippin.me][tippin-me-badge]][tippin-me-url]
11[![Follow me][follow-me-badge]][follow-me-url]
12
13[![Version][version-badge]][version-url]
14[![Node version][node-version-badge]][node-version-url]
15[![MIT License][mit-license-badge]][mit-license-url]
16
17[![Downloads][downloads-badge]][downloads-url]
18[![Total downloads][total-downloads-badge]][downloads-url]
19[![Packagephobia][packagephobia-badge]][packagephobia-url]
20[![Bundlephobia][bundlephobia-badge]][bundlephobia-url]
21
22[![CircleCI][circleci-badge]][circleci-url]
23[![Dependency Status][daviddm-badge]][daviddm-url]
24[![codecov][codecov-badge]][codecov-url]
25[![Coverage Status][coveralls-badge]][coveralls-url]
26
27[![codebeat badge][codebeat-badge]][codebeat-url]
28[![Codacy Badge][codacy-badge]][codacy-url]
29[![Code of Conduct][coc-badge]][coc-url]
30
31> 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.
32
33## Table of contents <!-- omit in toc -->
34
35- [Pre-requisites](#pre-requisites)
36 - [Node.js](#nodejs)
37 - [Browser](#browser)
38- [Setup](#setup)
39 - [Install](#install)
40 - [Usage](#usage)
41 - [TypeScript or native ES Modules](#typescript-or-native-es-modules)
42 - [Node.js](#nodejs-1)
43 - [Browser](#browser-1)
44 - [ES Modules](#es-modules)
45 - [UMD](#umd)
46- [Demo](#demo)
47- [API Reference](#api-reference)
48 - [OnfinishFulfilled&lt;T&gt;](#onfinishfulfilledlttgt)
49 - [OnfinishRejected](#onfinishrejected)
50 - [PollingMeasure](#pollingmeasure)
51 - [Methods](#methods)
52 - [PollingMeasure.toJSON()](#pollingmeasuretojson)
53 - [PollingObserver&lt;T&gt;](#pollingobserverlttgt)
54 - [Methods](#methods-1)
55 - [PollingObserver.observe(callback[, options])](#pollingobserverobservecallback-options)
56 - [PollingObserver.disconnect()](#pollingobserverdisconnect)
57 - [PollingObserver.takeRecords()](#pollingobservertakerecords)
58 - [Event handler](#event-handler)
59 - [PollingObserver.onfinish](#pollingobserveronfinish)
60- [deno](#deno)
61- [License](#license)
62
63## Pre-requisites
64
65### Node.js
66
67- [Node.js][nodejs-url] >= 8.16.0
68- [NPM][npm-url] >= 6.4.1 ([NPM][npm-url] comes with [Node.js][nodejs-url] so there is no need to install separately.)
69- [perf_hooks] (Added in `node@8.5.0` behind experimental flag)
70
71### Browser
72
73- [window.Performance]
74
75## Setup
76
77### Install
78
79```sh
80# Install via NPM
81$ npm install --save @reallyland/polling-observer
82```
83
84### Usage
85
86[Performance] API is strictly required before running any polling. To ensure `performance.now` is available globally on Node.js, you can do:
87
88```js
89/** Node.js */
90import { performance } from 'perf_hooks';
91
92global.performance = performance; // or globalThis.performance = performance;
93```
94
95#### TypeScript or native ES Modules
96
97```ts
98interface DataType {
99 status: 'complete' | 'in-progress';
100 items: Record<string, any>[];
101}
102
103import { PollingObserver } from '@reallyland/polling-observer';
104
105const obs = new PollingObserver((data/** list, observer */) => {
106 const { status, items } = data || {};
107 const itemsLen = (items && items.length) || 0;
108
109 /** Stop polling when any of the conditions fulfills */
110 return 'complete' === status || itemsLen > 99;
111});
112
113obs.observe(
114 async () => {
115 /** Polling callback - fetch resources */
116 const r = await fetch('https://example.com/api?key=123');
117 const d = await r.json();
118
119 return d;
120 },
121 /** Run polling (at least) every 2 seconds and timeout if it exceeds 30 seconds */
122 {
123 interval: 2e3,
124 timeout: 30e3,
125 }
126);
127
128/**
129 * When polling finishes, it will either fulfill or reject depending on the status:
130 *
131 * | Status | Returns |
132 * | ------- | --------- |
133 * | finish | <value> |
134 * | timeout | <value> |
135 * | error | <reason> |
136 *
137 */
138obs.onfinish = (data, records/**, observer */) => {
139 const { status, value, reason } = data || {};
140
141 switch (status) {
142 case 'error': {
143 console.error(`Polling fails due to: `, reason);
144 break;
145 }
146 case 'timeout': {
147 console.log(`Polling timeouts after 30 seconds: `, value);
148 break;
149 }
150 case 'finish':
151 default: {
152 console.log(`Polling finishes: `, value);
153 }
154 }
155
156 console.log(`Formatted polling records: `, records.map(n => n.toJSON()));
157 /**
158 * [
159 * {
160 * duration: 100,
161 * entryType: 'polling-measure',
162 * name: 'polling:0',
163 * startTime: 100,
164 * },
165 * ...
166 * ]
167 */
168
169 obs.disconnect(); /** Disconnect to clean up */
170};
171```
172
173#### Node.js
174
175```js
176const { PollingObserver } = require('@reallyland/polling-observer');
177
178const obs = new PollingObserver((data/** entries, observer */) => {
179 const { status, items } = data || {};
180 const itemsLen = (items && items.length) || 0;
181
182 /** Stop polling when any of the conditions fulfills */
183 return 'complete' === status || itemsLen > 99;
184});
185
186obs.observe(
187 async () => {
188 /** Polling callback - fetch resources */
189 const r = await fetch('https://example.com/api?key=123');
190 const d = await r.json();
191
192 return d;
193 },
194 /** Run polling (at least) every 2 seconds and timeout if it exceeds 30 seconds */
195 {
196 interval: 2e3,
197 timeout: 30e3,
198 }
199);
200
201/**
202 * When polling finishes, it will either fulfill or reject depending on the status:
203 *
204 * | Status | Returns |
205 * | ------- | --------- |
206 * | finish | <value> |
207 * | timeout | <value> |
208 * | error | <reason> |
209 *
210 */
211obs.onfinish = (data, entries/**, observer */) => {
212 const { status, value, reason } = data || {};
213
214 switch (status) {
215 case 'error': {
216 console.error(`Polling fails due to: `, reason);
217 break;
218 }
219 case 'timeout': {
220 console.log(`Polling timeouts after 30 seconds: `, value);
221 break;
222 }
223 case 'finish':
224 default: {
225 console.log(`Polling finishes: `, value);
226 }
227 }
228
229 console.log(`Formatted polling entries: `, entries.map(n => n.toJSON()));
230 /**
231 * [
232 * {
233 * duration: 100,
234 * entryType: 'polling-measure',
235 * name: 'polling:0',
236 * startTime: 100,
237 * },
238 * ...
239 * ]
240 */
241
242 obs.disconnect(); /** Disconnect to clean up */
243};
244```
245
246### Browser
247
248#### ES Modules
249
250```html
251<!doctype html>
252<html>
253 <head>
254 <script type="module">
255 import { PollingObserver } from 'https://unpkg.com/@reallyland/polling-observer@latest/dist/polling-observer.min.js';
256
257 // --snip
258 </script>
259 </head>
260</html>
261```
262
263#### UMD
264
265```html
266<!doctype html>
267<html>
268 <head>
269 <script src="https://unpkg.com/@reallyland/polling-observer@latest/dist/polling-observer.umd.min.js"></script>
270 <script>
271 var { PollingObserver } = window.PollingObserver;
272
273 // --snip
274 </script>
275 </head>
276</html>
277```
278
279## Demo
280
281[![Edit PollingObserver](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/pollingobserver-b269s?fontsize=14)
282
283## API Reference
284
285### OnfinishFulfilled&lt;T&gt;
286
287```ts
288interface OnfinishFulfilled<T> {
289 status: 'finish' | 'timeout';
290 value: T;
291}
292```
293
294### OnfinishRejected
295
296```ts
297interface OnfinishRejected {
298 status: 'error';
299 reason: Error;
300}
301```
302
303### PollingMeasure
304
305```ts
306interface PollingMeasure {
307 duration: number;
308 entryType: 'polling-measure';
309 name: string;
310 startTime: number;
311}
312```
313
314- `duration` <[number][number-mdn-url]> Duration of the polling takes in milliseconds.
315- `entryType` <[string][string-mdn-url]> Entry type, defaults to `polling-measure`.
316- `name` <[string][string-mdn-url]> Polling name in the format of `polling:<index>` where `<index>` starts from `0` and increments on each polling.
317- `startTime` <[string][string-mdn-url]> Relative timestamp (in milliseconds ) indicates when the polling starts at.
318
319#### Methods
320
321##### PollingMeasure.toJSON()
322
323- <[Function][function-mdn-url]> Returns a JSON representation of the polling object's properties.
324
325---
326
327### PollingObserver&lt;T&gt;
328
329- `conditionCallback` <[Function][function-mdn-url]> Condition callback to be executed in each polling and return the condition result in the type of boolean, e.g. return `true` to stop next poll.
330 - `data` <`T`> Polling data returned by `callback` in the type of `T` which defined in the [PollingObserver.observe()] method.
331 - `entries` <[Array][array-mdn-url]&lt;[PollingMeasure]&gt;> A list of [PollingMeasure] objects.
332 - `observer` <[PollingObserver]&lt;`T`&gt;> Created [PollingObserver] object.
333 - returns: <[boolean][boolean-mdn-url]> If `true`, the polling stops. Returning `false` will result in an infinite polling as the condition will never meet.
334- returns: <[PollingObserver]&lt;`T`&gt;> [PollingObserver] object.
335
336#### Methods
337
338##### PollingObserver.observe(callback[, options])
339
340The method is used to initiate polling with a polling callback and optional configuration.
341
342- `callback` <[Function][function-mdn-url]> Callback to be executed in each polling and return the result so that it will be passed as the first argument in `conditionCallback`.
343 - returns: <`T` | [Promise][promise-mdn-url]&lt;`T`&gt;> Return polling result in the type of `T` or `Promise<T>` in each polling.
344- `options` <[Object][object-mdn-url]> Optional configuration to run the polling.
345 - `interval` <[number][number-mdn-url]> Optional interval in milliseconds. This is the minimum delay before starting the next polling.
346 - `timeout` <[number][number-mdn-url]> Optional timeout in milliseconds. Polling ends when it reaches the defined timeout even though the condition has not been met yet. _As long as `timeout` 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._
347
348##### PollingObserver.disconnect()
349
350Once 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.
351
352A `onfinish` event handler can be used to retrieve polling records after a disconnection but it has to be attached before disconnecting the observer.
353
354##### PollingObserver.takeRecords()
355
356The method returns a list of [PollingMeasure] object containing the metrics of each polling.
357
358- returns: <[Array][array-mdn-url]&lt;[PollingMeasure]&gt;> A list of [PollingMeasure] objects.
359
360#### Event handler
361
362##### PollingObserver.onfinish
363
364_Note that no `finish` event fires when the polling finishes. So `observer.addEventListener('finish', ...)` will not work._
365
366Event 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.
367
368When a polling fulfills, it returns an [OnfinishFulfilled&lt;T&gt;] object with `status` set to `finish` or `timeout` and a `value` in the type of `T`.
369
370When a polling rejects, it returns an [OnfinishRejected] object with `status` set to `error` and a `reason` in the type of [Error][error-mdn-url].
371
372| Status | Returns |
373| --------- | -------------- |
374| `finish` | &lt;value&gt; |
375| `timeout` | &lt;value&gt; |
376| `error` | &lt;reason&gt; |
377
378## deno
379
380_Coming soon._
381
382## License
383
384[MIT License](https://motss.mit-license.org/) © Rong Sen Ng (motss)
385
386<!-- References -->
387[typescript-url]: https://github.com/Microsoft/TypeScript
388[nodejs-url]: https://nodejs.org
389[npm-url]: https://www.npmjs.com
390[node-releases-url]: https://nodejs.org/en/download/releases
391[perf_hooks]: https://nodejs.org/api/perf_hooks.html
392[Performance]: https://developer.mozilla.org/en-US/docs/Web/API/Performance
393[window.performance]: https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
394
395[PollingObserver]: #pollingobservert
396[PollingObserver.observe()]: #pollingobserverobservecallback-options
397[PollingObserver.disconnect()]: #pollingobserverdisconnect
398[PollingObserver.takeRecords()]: #pollingobservertakerecords
399[PollingMeasure]: #pollingmeasure
400[PollingMeasure.toJSON()]: #pollingmeasuretojson
401[OnfinishFulfilled&lt;T&gt;]: #onfinishfulfilledt
402[OnfinishRejected]: #onfinishrejected
403
404<!-- MDN -->
405[array-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
406[boolean-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean
407[function-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
408[map-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
409[number-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
410[object-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
411[promise-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
412[regexp-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
413[set-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
414[string-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
415[void-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
416[error-mdn-url]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
417
418<!-- Badges -->
419[tippin-me-badge]: https://badgen.net/badge/%E2%9A%A1%EF%B8%8Ftippin.me/@igarshmyb/F0918E
420[follow-me-badge]: https://flat.badgen.net/twitter/follow/igarshmyb?icon=twitter
421
422[version-badge]: https://flat.badgen.net/npm/v/@reallyland/polling-observer?icon=npm
423[node-version-badge]: https://flat.badgen.net/npm/node/@reallyland/polling-observer
424[mit-license-badge]: https://flat.badgen.net/npm/license/@reallyland/polling-observer
425
426[downloads-badge]: https://flat.badgen.net/npm/dm/@reallyland/polling-observer
427[total-downloads-badge]: https://flat.badgen.net/npm/dt/@reallyland/polling-observer?label=total%20downloads
428[packagephobia-badge]: https://flat.badgen.net/packagephobia/install/@reallyland/polling-observer
429[bundlephobia-badge]: https://flat.badgen.net/bundlephobia/minzip/@reallyland/polling-observer
430
431[circleci-badge]: https://flat.badgen.net/circleci/github/reallyland/polling-observer?icon=circleci
432[daviddm-badge]: https://flat.badgen.net/david/dep/reallyland/polling-observer
433[codecov-badge]: https://flat.badgen.net/codecov/c/github/reallyland/polling-observer?label=codecov&icon=codecov
434[coveralls-badge]: https://flat.badgen.net/coveralls/c/github/reallyland/polling-observer?label=coveralls
435
436[codebeat-badge]: https://codebeat.co/badges/8400d381-c10e-4d5a-a00b-9478b50eb129
437[codacy-badge]: https://api.codacy.com/project/badge/Grade/e9ea6e2433c5449b8151a4606fd60148
438[coc-badge]: https://flat.badgen.net/badge/code%20of/conduct/pink
439
440<!-- Links -->
441[tippin-me-url]: https://tippin.me/@igarshmyb
442[follow-me-url]: https://twitter.com/igarshmyb?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=reallyland/polling-observer
443
444[version-url]: https://www.npmjs.com/package/@reallyland/polling-observer
445[node-version-url]: https://nodejs.org/en/download
446[mit-license-url]: https://github.com/reallyland/polling-observer/blob/master/LICENSE
447
448[downloads-url]: https://www.npmtrends.com/@reallyland/polling-observer
449[packagephobia-url]: https://packagephobia.now.sh/result?p=%40reallyland%2Fpolling-observer
450[bundlephobia-url]: https://bundlephobia.com/result?p=@reallyland/polling-observer
451
452[circleci-url]: https://circleci.com/gh/reallyland/polling-observer/tree/master
453[daviddm-url]: https://david-dm.org/reallyland/polling-observer
454[codecov-url]: https://codecov.io/gh/reallyland/polling-observer
455[coveralls-url]: https://coveralls.io/github/reallyland/polling-observer?branch=master
456
457[codebeat-url]: https://codebeat.co/projects/github-com-reallyland-polling-observer-master
458[codacy-url]: https://www.codacy.com/app/motss/polling-observer?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=reallyland/polling-observer&amp;utm_campaign=Badge_Grade
459[coc-url]: https://github.com/reallyland/polling-observer/blob/master/CODE-OF-CONDUCT.md