Skip to content

Commit 7d86b56

Browse files
authored
Merge pull request #6 from AddSearch/throttled-search
Throttled search
2 parents 9d8dc11 + 82ef28c commit 7d86b56

File tree

4 files changed

+121
-3
lines changed

4 files changed

+121
-3
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,12 @@ client.setPersonalizationEvents(events);
260260
client.setJWT(token);
261261
```
262262

263+
#### Set API throttling
264+
```js
265+
// Set API call throttle time in milliseconds. Default is 200.
266+
client.setThrottleTime(500);
267+
```
268+
263269
## Supported web browsers and node.js versions
264270
The client is tested on
265271
- Chrome

src/index.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ var executeApiFetch = require('./apifetch');
44
var sendStats = require('./stats');
55
var Settings = require('./settings');
66
var util = require('./util');
7+
var throttle = require('./throttle');
8+
9+
var API_THROTTLE_TIME_MS = 200;
710

811
var client = function(sitekey) {
912
this.sitekey = sitekey;
@@ -44,7 +47,11 @@ var client = function(sitekey) {
4447

4548
this.settings.setCallback(callback);
4649
this.settings.setKeyword(keyword);
47-
executeApiFetch(this.sitekey, 'search', this.settings.getSettings(), callback);
50+
51+
if (!this.throttledSearchFetch) {
52+
this.throttledSearchFetch = throttle(API_THROTTLE_TIME_MS, executeApiFetch);
53+
}
54+
this.throttledSearchFetch(this.sitekey, 'search', this.settings.getSettings(), callback);
4855
}
4956

5057

@@ -58,7 +65,11 @@ var client = function(sitekey) {
5865
throw "Illegal suggestions parameters. Should be (prefix, callbackFunction)";
5966
}
6067
this.settings.setSuggestionsPrefix(prefix);
61-
executeApiFetch(this.sitekey, 'suggest', this.settings.getSettings(), callback);
68+
69+
if (!this.throttledSuggestionsFetch) {
70+
this.throttledSuggestionsFetch = throttle(API_THROTTLE_TIME_MS, executeApiFetch);
71+
}
72+
this.throttledSuggestionsFetch(this.sitekey, 'suggest', this.settings.getSettings(), callback);
6273
}
6374

6475

@@ -72,7 +83,11 @@ var client = function(sitekey) {
7283
throw "Illegal autocomplete parameters. Should be (field, prefix, callbackFunction)";
7384
}
7485
this.settings.setAutocompleteParams(field, prefix);
75-
executeApiFetch(this.sitekey, 'autocomplete', this.settings.getSettings(), callback);
86+
87+
if (!this.throttledAutocompleteFetch) {
88+
this.throttledAutocompleteFetch = throttle(API_THROTTLE_TIME_MS, executeApiFetch);
89+
}
90+
this.throttledAutocompleteFetch(this.sitekey, 'autocomplete', this.settings.getSettings(), callback);
7691
}
7792

7893

@@ -101,6 +116,7 @@ var client = function(sitekey) {
101116
this.setShuffleAndLimitTo = function(shuffleAndLimitTo) { this.settings.setShuffleAndLimitTo(shuffleAndLimitTo); }
102117
this.setFuzzyMatch = function(fuzzy) { this.settings.setFuzzyMatch(fuzzy); }
103118
this.setCollectAnalytics = function(collectAnalytics) { this.settings.setCollectAnalytics(collectAnalytics); }
119+
this.setThrottleTime = function(delay) { API_THROTTLE_TIME_MS = delay; }
104120
this.setStatsSessionId = function(id) { this.sessionId = id; }
105121
this.getStatsSessionId = function() { return this.sessionId; }
106122

src/throttle.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
var throttle = function(delay, callback) {
2+
3+
// last time callback was executed.
4+
var lastExec = 0;
5+
6+
// returned by setTimeout
7+
var timeout;
8+
9+
// Clear existing timeout
10+
function clearExistingTimeout() {
11+
if (timeout) {
12+
clearTimeout(timeout);
13+
}
14+
}
15+
16+
/*
17+
* Wrap the callback inside a throttled function
18+
*/
19+
function wrapper() {
20+
21+
var self = this;
22+
var elapsed = Date.now() - lastExec;
23+
var args = arguments;
24+
25+
// Execute callback function
26+
function exec() {
27+
lastExec = Date.now();
28+
callback.apply(self, args);
29+
}
30+
31+
clearExistingTimeout();
32+
33+
// Execute
34+
if (elapsed > delay) {
35+
exec();
36+
}
37+
// Schedule for a later execution
38+
else {
39+
timeout = setTimeout(exec, delay - elapsed);
40+
}
41+
}
42+
43+
// Return the wrapper function.
44+
return wrapper;
45+
}
46+
47+
module.exports = throttle;

test/throttle.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
var assert = require('assert');
2+
var throttle = require('../src/throttle');
3+
4+
function sleep(ms) {
5+
return new Promise(resolve => setTimeout(resolve, ms));
6+
}
7+
8+
describe('throttle', function() {
9+
10+
it('function shouldn\'t be throttled (30 function calls in 600ms)', async function() {
11+
this.timeout(5000);
12+
var functionCalls = 30;
13+
var sleepTime = 20; // 30*20 = 600 ms
14+
var expectedExecutions = 30; // Function called in every iteration
15+
var executions = 0;
16+
17+
var f = function() {
18+
executions++;
19+
};
20+
21+
for (var i=0; i<functionCalls; i++) {
22+
f();
23+
await sleep(sleepTime);
24+
}
25+
26+
assert.equal(expectedExecutions, executions);
27+
});
28+
29+
it('function should be throttled (4 function calls in 600ms', async function() {
30+
this.timeout(5000);
31+
var functionCalls = 30;
32+
var sleepTime = 20; // 30*20 = 600 ms
33+
var throttleDelay = 200;
34+
var expectedExecutions = 3 + 1; // Call functions for 600 ms with 200 ms throttle = 3 calls + one last call
35+
var executions = 0;
36+
37+
var f = throttle(throttleDelay, function() {
38+
executions++;
39+
});
40+
41+
for (var i=0; i<functionCalls; i++) {
42+
f();
43+
await sleep(sleepTime);
44+
}
45+
46+
assert.equal(expectedExecutions, executions);
47+
});
48+
49+
});

0 commit comments

Comments
 (0)