Last active
September 17, 2018 08:37
-
-
Save levynir/2d4054c2f988947090aeb48459966fcc to your computer and use it in GitHub Desktop.
axios debounce cache demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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