I want to poll an endpoint no faster than once a second, and no slower than the time it takes to poll the endpoint. There should never be more than one request outstanding.
I want a reactive programming way to poll an endpoint at least once a second, but if the endpoint takes longer than 1 second, the next request fires immediately.
In the marble diagram below, the 2nd and 3rd requests take longer than 1 second, but the 4th and 5th requests finish quicker. The next request fires either on the 1 second boundary, or immediately upon obtaining the data from the last outstanding request.
s---s---s---s---s---s---| # 1 second interval observable
r---r----r--------r-r---| # endpoint begin polling events
-d-------d--------dd-d--| # endpoint data response events
I'm trying to get the terminology correct in the marble diagram, so I'm assuming that the beginning of the endpoint requests should be the marble I label "r", and the marble event I label "d" has the endpoint data.
Here's how much code it took me to do this in plain js; however, the subsequent requests do not fire immediately upon being obtained as I have asked above.
var poll;
var previousData;
var isPolling = false;
var dashboardUrl = 'gui/metrics/dashboard';
var intervalMs = updateServiceConfig.getIntervalInMilliSecondForCharts();
return {
startInterval: startInterval,
stopInterval: stopInterval
};
function startInterval() {
stopInterval();
tryPolling(); // immediately hit the dashboard
// attempt polling at the interval
poll = $interval(tryPolling, intervalMs);
}
/**
* attempt polling as long as there is no in-flight request
* once the in-flight request completes or fails, allow the next request to be processed
*/
function tryPolling() {
if (!isPolling) {
isPolling = true;
getDashboard()
// if the dashboard either returns successful or fails, reset the polling boolean
.then(resetPolling, resetPolling);
}
}
/** there's no longer an in-flight request, so reset the polling boolean */
function resetPolling() {
isPolling = false;
}
function stopInterval() {
if (poll) {
$interval.cancel(poll);
poll = undefined;
}
}
function getDashboard() {
return restfulService.get(dashboardUrl)
.then(updateDashboard);
}
function updateDashboard(data) {
if (!utils.deepEqual(data, previousData)) {
previousData = angular.copy(data);
$rootScope.$broadcast('$dashboardLoaded', data);
}
}