Skip to content

Instantly share code, notes, and snippets.

@bodia-uz
Last active September 8, 2017 09:32
Show Gist options
  • Save bodia-uz/d9a3be296819875fb6958c4ef9d03667 to your computer and use it in GitHub Desktop.
Save bodia-uz/d9a3be296819875fb6958c4ef9d03667 to your computer and use it in GitHub Desktop.
js http polling
import Backoff from 'backo2';
import { isFunction } from 'lodash';
import makeCancelable from './makeCancelable';
function createPolling(fn, pollInterval, { shouldKeepPolling } = {}) {
let intervalId;
let keepPolling = false;
let fnPromise;
// use backoff when connection lost
const backoff = new Backoff({
min: pollInterval,
jitter: 0.5
});
function fnWithInterval(fnArguments) {
if (keepPolling && isFunction(shouldKeepPolling)) {
keepPolling = shouldKeepPolling(...fnArguments);
}
if (!keepPolling) {
return;
}
let fnResult = fn(...fnArguments);
// if type of fn result is not promise, wrap it in promise
if (!fnResult || (fnResult && !isFunction(fnResult.then))) {
fnResult = Promise.resolve(fnResult);
}
fnPromise = makeCancelable(fnResult);
fnPromise.promise
.then(() => {
// reset backoff if connection successful
backoff.reset();
intervalId = setTimeout(fnWithInterval, pollInterval, fnArguments);
})
.catch(error => {
if (error && error.isCanceled) {
// promise is canceled, stop fn polling
return;
}
// use incremental (backoff) polling interval if connection lost
intervalId = setTimeout(
fnWithInterval,
backoff.duration(),
fnArguments
);
});
}
function start(...fnArguments) {
stop();
keepPolling = true;
fnWithInterval(fnArguments);
}
function stop() {
keepPolling = false;
if (intervalId) {
clearTimeout(intervalId);
intervalId = null;
}
if (fnPromise) {
fnPromise.cancel();
fnPromise = null;
}
}
return {
start: start,
restart: start,
stop: stop,
isStarted() {
return keepPolling;
},
setInterval(interval) {
pollInterval = interval;
backoff.setMin(interval);
}
};
}
export default createPolling;
import React, { Component } from 'react';
import isEqual from 'lodash/isEqual';
import ItemsList from './ItemsList';
import createPolling from './createPolling';
class Items extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
items: []
};
}
fetchItems(query) {
return fetch('/data.json')
.then(response => response.json())
.then(items => {
const visibleItems = (
query ?
items.filter(item => item.name.indexOf(query) !== -1) :
items
);
console.log(items.length, visibleItems.length);
this.setState({items: visibleItems})
})
}
componentDidMount() {
this.polling = createPolling(this.fetchItems.bind(this), 1000);
this.polling.start(this.state.query);
}
shouldComponentUpdate(nextProps, nextState) {
return (
!isEqual(this.props, nextProps) ||
!isEqual(this.state, nextState)
);
}
componentWillUnmount() {
this.polling.stop();
}
render() {
return (
<div>
<input type="text" value={this.state.query} onChange={e => {
this.setState({query: e.target.value});
this.polling.restart(e.target.value);
}}/>
<ItemsList items={this.state.items}/>
</div>
);
}
}
export default Items;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment