I have a really strange effect in a web app I wrote. The number of current search results has a huge impact on the speed of the next search, even though the search does in no way use the result list.
I'm not sure how much code I should post here to demonstrate what the app does, but the whole app is online at http://connective-lex.info. (To see the effect, select all lexicons, then vary the search options, e.g., check one or none.)
This is the query function. The first line, marked !!!
, clears the previous result list, resulting in really fast searches. If this line is commented out, the search starts a little faster, but then is extremely slow if many items are in the previous result list.
The result list is this.results
, and isn't used in the search code. It also isn't used in the ResultsFilter class which does the actual filtering, which I omit for brevity but will happily post more code if you need more.
// File: c24-components.js
// Class: ResultsComponent
ExecuteQuery() {
setTimeout(() => this.SetResults([]), 0); // !!!
let tempResults = [];
let lexIds = Object.keys(gSelectorComponent.lex);
let totalSize = lexIds.map(lexId => gSelectorComponent.lex[lexId].entry.length).
reduce((acc, val) => acc + val, 0);
let resultsFilter = new ResultsFilter(totalSize);
let processAtOnce = 20;
this.activeSearches.forEach(timeoutId => clearTimeout(timeoutId));
this.activeSearches.length = 0;
/* Subfunction which emulates asynchronous processing in the (single-threaded)
JS environment by processing a slice of entries and then enqueuing the next
slice to be processed so that the browser has time to process user events
and render the GUI between slices. */
let processingFunction = (lexIdIndex, entryIndex) => {
if (lexIdIndex >= lexIds.length) {
this.activeSearches.push(setTimeout(() => this.SetResults(tempResults), 0));
return;
}
let entries = undefined;
try {
entries = gSelectorComponent.lex[lexIds[lexIdIndex]].entry;
} catch (e) {
// This happens when a lexicon is deselected while a search is running.
// Abort search.
return;
}
for (let i = 0; i < processAtOnce; ++i, ++entryIndex) {
if (entryIndex >= entries.length) {
this.activeSearches.push(setTimeout(processingFunction, 0, ++lexIdIndex, 0));
return;
}
if (resultsFilter.TestEntry(entries[entryIndex])) {
tempResults.push(entries[entryIndex]);
}
}
this.activeSearches.push(setTimeout(processingFunction, 0, lexIdIndex,
entryIndex));
};
this.activeSearches.push(setTimeout(processingFunction, 0, 0, 0));
}
Update: If I interpret Chrome's Performance tools correctly, this is a reflow issue caused by updating the progress bar. On every update, JS spends a lot of time in "Update Layer Tree" rendering operations, which seems to be the only difference which takes longer if more previous results are displayed. I wonder how I can get rid of this effect but still display progress.