// Dependencies
const events = require('events')
// Includes
const settings = require('../../settings.json')
const promiseTimeout = require('../internal/timeout')
// Args
exports.required = ['getLatest', 'delay']
exports.optional = ['timeout']
// Docs
/**
* @typedef {function} getLatest
* @param {number} latest - A value representing the latest version.
* @param {EventEmitter} event - The event emitter to emit to.
*/
/**
* ✅ This is the base for events that do not rely on true streams. The `getLatest` function receives some value that represents the latest version of something (eg. a date or unique ID) and determines if there is new information, every time it is fired it waits `delay` ms before being fired again. Every time it must return an object with the field `latest`, representing the latest value (which will not change if new information was not received), and an array `data` which has the new values (if there are multiple they each have their own index, if there is only one then it is by itself in the array). If `latest` is equal to -2, the returned data will be processed even if it is the initial run (which usually only establishes the latest value). If the return object has a true `repeat` value, the function latest will be run again immediately after. If `delay` is a string it will take the number from that string key in the `event` object of the settings.json file.
* When the function is first called it will initialize `getLatest` with the value -1 and then emit the `connect` event. Whenever data is received, it will emit the `data` event for each value. If the `close` event is emitted the function will no longer run. If an error occurs the `error` event will be emitted, the function will log a retry and after the number of max retries as specified by settings, it will emit the `close` event.
* The `getLatest` function will be marked as failed if it does not resolve within `timeout` ms (which can be disabled if timeout is negative). If getLatest fails for any reason (including timeout) it will be retried `maxRetries` times before stopping.
* @category Utility
* @alias shortPoll
* @param {function} getLatest - The function to use to get the latest. Should return an object with key 'data' - an array containing output data,
* and the new 'latest' value.
* @returns {Promise<GetLatestResponse>}
**/
// Define
exports.func = function (args) {
const latest = args.getLatest
let delay = args.delay
delay = (typeof delay === 'string' || delay instanceof String ? settings.event[delay] : delay) || settings.event.defaultDelay
let retries = 0
const max = settings.event.maxRetries
const timeout = args.timeout || settings.event.timeout
let stop = false
let current
const evt = new events.EventEmitter()
const run = function (value) {
if (stop) {
return
}
let promise = latest(value, evt)
if (timeout > 0) {
promise = promiseTimeout(promise, timeout)
}
return promise.then(function (response) {
if (stop) {
return
}
if (value === -1) {
current = response.latest
}
retries = 0
const data = response.data
if (data.length > 0 && (value !== -1 || current === -2)) {
current = response.latest
for (let i = 0; i < data.length; i++) {
evt.emit('data', data[i])
}
}
if (response.repeat) {
run(current)
} else {
setTimeout(run, delay, current)
}
return response
})
.catch(function (err) {
if (stop) {
return
}
evt.emit('error', err)
retries++
if (retries > max) {
evt.emit('close', new Error('Max retries reached'))
} else {
setTimeout(run, delay, current)
}
})
}
run(-1)
.then(function (response) {
if (stop) {
return
}
evt.emit('connect', response.latest)
})
.catch(function (err) {
evt.emit('close', new Error('Initialization failed: ' + err.message))
})
evt.on('close', function (err) {
stop = true
if (err) {
evt.emit('error', err)
}
})
return evt
}
Source