Skip to content

Instantly share code, notes, and snippets.

@freaktechnik
Created October 1, 2018 23:13
Show Gist options
  • Save freaktechnik/9fa0e1eda076ade929d30d215506c8fc to your computer and use it in GitHub Desktop.
Save freaktechnik/9fa0e1eda076ade929d30d215506c8fc to your computer and use it in GitHub Desktop.
class PollEndpoint {
constructor(aOpts = {}) {
this.poll = aOpts.poll;
this.state = Object.assign({
isVisible: false, // timeline visibility (as in, are individual tweets visible)
isFocused: false, // IM tab focus
rateLimit: Infinity,
remaining: Infinity,
resetTime: Infinity,
requestsPerPoll: 1,
hasUnread: false,
}, aOpts.state);
this.lastPoll = 0;
this.lastInterval = 0;
this.isInterval = false;
this.stopWhenInvisible = aOpts.stopWhenInvisible || false;
this.stopWhenNotFocused = aOpts.stopWhenNotFocused || this.stopWhenInvisible;
this.stopWhenUnreadAndNotFocused = aOpts.stopWhenUnreadAndNotFocused || this.stopWhenNotFocused;
this.startPolling();
}
getSecondsUntilNextAllowedRequest() {
const rateLimitSet = !isNaN(this.state.rateLimit) && Number.isFinite(this.state.rateLimit);
// Ignore any rate limit info if it's not actual info.
if (rateLimitSet) {
const secondsUntilReset = (this.state.resetTime - Date.now()) / 1000;
// Ignore stored rate limit info if it is expired
if (secondsUntilReset < 0)
return 0;
return this.state.requestsPerPoll * Math.ceil(secondsUntilReset / this.state.remaining);
}
return 0;
}
getPollInterval() {
const secondsUntilNextAllowedRequest = this.getSecondsUntilNextAllowedRequest();
// Determine minimum poll rate from state.
let minSeconds = 600; // 10 mins.
if (this.state.isFocused) {
if (this.state.isVisible) {
minSeconds = 10; // 10 seconds
} else {
minSeconds = 60; // 3 mins
}
}
// This tries to never go above the rate limit by spacing out requests until the rate limit resets.
return Math.max(minSeconds, secondsUntilNextAllowedRequest) * 1000;
}
doPoll() {
this.lastPoll = Date.now();
if (this.poll) {
this.state.remaining--;
this.poll();
}
}
clearTimeout() {
if (this.timeout) {
if (this.isInterval) {
clearInterval(this.timeout);
} else {
clearTimeout(this.timeout);
}
}
}
setTimeout(aInterval, aDelay = 0) {
dump("setting timeout for "+aInterval+" with a delay of "+aDelay+"\n");
this.lastInterval = aInterval;
this.isInterval = aDelay === 0;
if (this.isInterval) {
this.timeout = setInterval(() => this.doPoll(), this.lastInterval);
} else {
this.timeout = setTimeout(() => this.doPoll(), aDelay);
}
}
startPolling() {
const newInterval = this.getPollInterval();
if (newInterval !== this.lastInterval || !this.isInterval) {
this.clearTimeout();
this.setTimeout(newInterval);
}
}
updateState(aNewState) {
// Ensure integrity of new state
if (aNewState.isVisible) {
aNewState.hasUnread = false;
if (!aNewState.isFocused)
aNewState.isFocused = aNewState.isVisible;
}
else if (aNewState.hasOwnProperty('isFocused') && !aNewState.isFocused)
aNewState.isVisible = false;
// Update state, allow partial updates.
Object.assign(this.state, aNewState);
const newInterval = this.getPollInterval();
if (newInterval !== this.lastInterval) {
let delay = undefined;
// Wait until rate limit has refreshed if the bucket is exhausted
if (this.state.remaining == 0) {
delay = this.state.resetTime - Date.now();
}
// Immediately shorten the poll interval
if (this.lastPoll > 0) {
delay = Math.max(delay ? delay : 0, newInterval - Date.now() + this.lastPoll, 1);
}
this.clearTimeout();
if ((!this.state.hasUnread || this.state.isFocused || !this.stopWhenUnreadAndNotFocused) &&
(this.state.isFocused || !this.stopWhenNotFocused) &&
(this.state.isVisible || !this.stopWhenInvisible))
this.setTimeout(newInterval, delay);
else
dump("Stopped polling\n");
}
}
updateStateFromRequest(aRequest) {
this.updateState({
rateLimit: parseInt(aRequest.getResponseHeader('x-rate-limit-limit'), 10),
remaining: parseInt(aRequest.getResponseHeader('x-rate-limit-remaining'), 10),
resetTime: parseInt(aRequest.getResponseHeader('x-rate-limit-reset'), 10) * 1000,
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment