Skip to content

Instantly share code, notes, and snippets.

@levynir
Last active September 17, 2018 08:37
Show Gist options
  • Save levynir/2d4054c2f988947090aeb48459966fcc to your computer and use it in GitHub Desktop.
Save levynir/2d4054c2f988947090aeb48459966fcc to your computer and use it in GitHub Desktop.
axios debounce cache demo
import axios from 'axios';
import {MD5} from 'object-hash'; //npm install object-hash
const hash=MD5; //MD5 is less secure but much faster on my production linux machines
export class ServerConnection {
static _defaultOptions = {debounce: true, ttl:1000};
static _once = {};
static cache(func,options,...params) {
options = options || ServerConnection._defaultOptions;
const cachekey = hash(params);
if (!ServerConnection._once[cachekey] || options['debounce']===false) {
ServerConnection._once[cachekey] = func(...params);
return ServerConnection._once[cachekey];
} else {
setTimeout( ()=>(delete ServerConnection._once[cachekey]), options.ttl );
return ServerConnection._once[cachekey];
}
}
/**
* POST an object to the main server.
* Requests are debounced (cached) by default within 1-second timeframe!
* This means that multiple calls to this method with the same parameters
* will return the result of the first call within the last 1-second.
*
* @method
* @endpoint {String} URL
* @data {Object} data to POST to the server
* @options {Object} debounce options: defaults are {debounce: true, ttl:1000}
* @return {Promise} Promise
*/
static post(endpoint, data, options) {
return ServerConnection.cache(axios.post,options,endpoint,data);
}
/**
* GET a url from the main server.
* Requests are debounced (cached) by default within 1-second timeframe!
* This means that multiple calls to this method with the same parameters
* will return the result of the first call within the last 1-second.
*
* @method
* @endpoint {String} URL
* @options {Object} debounce options: defaults are {debounce: true, ttl:1000}
* @return {Promise} Promise
*/
static get(endpoint, options) {
return ServerConnection.cache(axios.get,options,endpoint);
}
}
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const should = chai.should();
const expect = chai.expect;
import { ServerConnection } from '../services/lib/rest-bridge';
const localhost = 'http://localhost/'; //localhost should return current timestamp in microseconds
function check_array_equal(results) {
let eq = false;
for (let i=1; i<results.length; i++) {
eq = results[i] === results[0];
}
return eq;
}
describe('Debounce ServerConnection', function() {
it('Should do simple GET request', function () {
return ServerConnection.get(localhost)
.then( x => x.should.not.be.null );
});
it('Should do multiple GET request WITHOUT debounce', function () {
return Promise.all([
ServerConnection.get(localhost,{debounce:false}),
ServerConnection.get(localhost,{debounce:false}),
ServerConnection.get(localhost,{debounce:false}),
ServerConnection.get(localhost,{debounce:false}),
ServerConnection.get(localhost,{debounce:false}),
]).then(results => {
results[0].should.not.be.null;
check_array_equal(results).should.be.false;
});
});
it('Should do multiple GET request with explicit ttl', function () {
return Promise.all([
ServerConnection.get(localhost,{ttl:1}),
ServerConnection.get(localhost,{ttl:1}),
ServerConnection.get(localhost,{ttl:1}),
ServerConnection.get(localhost,{ttl:1}),
ServerConnection.get(localhost,{ttl:1}),
]).then(results => {
results[0].should.not.be.null;
results.should.have.length.of(5);
//we can not expect results to be the same or different
//since 1ms ttl is not reliable
});
});
it('Should do multiple GET request WITH debounce', function () {
return Promise.all([
ServerConnection.get(localhost),
ServerConnection.get(localhost),
ServerConnection.get(localhost),
ServerConnection.get(localhost),
ServerConnection.get(localhost),
]).then(results => {
results[0].should.not.be.null;
check_array_equal(results).should.be.true;
});
});
it('Should expire debounce cache after 1 second', function () {
return Promise.all([
ServerConnection.get(localhost),
ServerConnection.get(localhost),
ServerConnection.get(localhost),
ServerConnection.get(localhost),
new Promise( (resolve,reject) => {
setttl( ()=>{
ServerConnection.get(localhost)
.then( x => resolve(x))
.catch( e => reject(e));
}, 1000);
}),
]).then(results => {
results[0].should.not.be.null;
check_array_equal(results).should.be.false;
});
});
it('Should check failed GET requests', function () {
return Promise.all([
ServerConnection.get(localhost+'404/error').catch(error => error.status),
ServerConnection.get(localhost+'404/error').catch(error => error.status),
ServerConnection.get(localhost+'404/error').catch(error => error.status),
ServerConnection.get(localhost+'404/error').catch(error => error.status),
ServerConnection.get(localhost+'404/error').catch(error => error.status),
]).then(results => {
expect(results[0]).to.equal(404);
check_array_equal(results).should.be.true;
return ServerConnection.get(localhost+'404/error');
}).then(results => {
expect(results).to.be.null; //should never get here
}).catch(error => {
expect(error.status).to.equal(404);
});
});
it('Should do simple POST request', function () {
return ServerConnection.post(localhost,{something:'somethingelse'})
.then( x => x.should.not.be.null );
});
it('Should do multiple POST request WITHOUT debounce', function () {
return Promise.all([
ServerConnection.post(localhost,{something:'somethingelse'},{debounce:false}),
ServerConnection.post(localhost,{something:'somethingelse'},{debounce:false}),
ServerConnection.post(localhost,{something:'somethingelse'},{debounce:false}),
ServerConnection.post(localhost,{something:'somethingelse'},{debounce:false}),
ServerConnection.post(localhost,{something:'somethingelse'},{debounce:false}),
]).then(results => {
results[0].should.not.be.null;
check_array_equal(results).should.be.false;
});
});
it('Should do multiple POST request WITH debounce', function () {
return Promise.all([
ServerConnection.post(localhost,{something:'somethingelse'}),
ServerConnection.post(localhost,{something:'somethingelse'}),
ServerConnection.post(localhost,{something:'somethingelse'}),
ServerConnection.post(localhost,{something:'somethingelse'}),
ServerConnection.post(localhost,{something:'somethingelse'}),
]).then(results => {
results[0].should.not.be.null;
check_array_equal(results).should.be.true;
});
});
it('Should not cache POST requests with different params', function () {
return Promise.all([
ServerConnection.post(localhost,{something:'somethingelse1'}),
ServerConnection.post(localhost,{something:'somethingelse2'}),
ServerConnection.post(localhost,{something:'somethingelse3'}),
ServerConnection.post(localhost,{something:'somethingelse4'}),
ServerConnection.post(localhost,{something:'somethingelse5'}),
]).then(results => {
results[0].should.not.be.null;
check_array_equal(results).should.be.false;
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment