Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Enhanced version of `docSearcher` webWorker discussed in Pete Darwin's blog [AngularJS Docs Performance](http://www.bacondarwin.co.uk/angularjs-docs-performance/)
angular.module('search', [])
.service('docsSearch', ['$q','$rootScope','$timeout','NG_PAGES',BackgroundSearchService]);
/**
* Document search service that uses Web Workers to index
* and search in the background.
*/
function BackgroundSearchService($q, $rootScope, $timeout, NG_PAGES) {
console.log('Using WebWorker Search Index');
// Create persistent instance...
var searcher = new PageSearcher($q, $rootScope, NG_PAGES);
// API that is performant & logical for multiple, sequential queries
return function search(qry) {
return searcher.find(qry);
};
/**
* Create a specialized webWorker to index pages
* and support multiple queries
*
* @returns {{find: function(query) }}
* @constructor
*/
function PageSearcher() {
var worker = initialize('js/search-worker.js');
// Publish simple API
return {
find: processQuery
};
/**
* Add the query to the query queue
* @param qry
* @returns {*}
*/
function processQuery(qry) {
var results = $q.defer();
// Chain each search query...
worker.$queue = worker.$queue
.then(function startQuery() {
return postMessage(qry)
})
.then(function onResponse(pages) {
results.resolve(pages);
});
return results.promise;
}
/**
* Reset the worker.onmessage to send a message when it has
* completed a search query and the results are available
*
* @param qry
* @returns {*}
*/
function postMessage(qry) {
var results = $q.defer();
// Reset the callback...
worker.onmessage = function (oEvent) {
$rootScope.$apply(function () {
switch (oEvent.data.e) {
case 'query-ready':
var pages = oEvent.data.d.map(function (path) {
return NG_PAGES[path];
});
results.resolve(pages);
break;
default :
throw new Error("unknown error");
}
});
};
worker.postMessage({q: qry});
return results.promise;
}
/**
* Initialize a WebWorker and listen for `index-ready` to
* announce ready to run a query.
*
* NOTE: Decorate the worker with a special processing `query` queue
*
* @returns {Worker}
*/
function initialize(path, failAfter) {
var startup = $q.defer();
var timer = $timeout(function() {
startup.reject( "Worker(" + path + ") is not responding..." );
}, failAfter || 5000, false);
try {
var worker = new Worker(path);
worker.$queue = startup.promise;
worker.onmessage = function (oEvent) {
switch (oEvent.data.e) {
case 'index-ready':
$timeout.cancel(timer);
startup.resolve();
break;
}
};
} catch( e ) {
$timeout.cancel(timer);
startup.reject( e.message );
}
return worker;
}
}
}
@petebacondarwin

This comment has been minimized.

Copy link

@petebacondarwin petebacondarwin commented Nov 20, 2014

Hi Thomas

Thanks for doing this.

I like where this is going. The idea, I think, is that you are using the chain of promises as a kind of FIFO queue since each time a 'query-ready' message arrives, it resolves the next deferred object in the promise chain.

And since all the promises are guaranteed (I think) to be resolved there is no issue with memory leakage.

I want to think about this a little more before I try implementing it on the docs site.

Pete

@ThomasBurleson

This comment has been minimized.

Copy link
Owner Author

@ThomasBurleson ThomasBurleson commented Nov 20, 2014

... I updated the initialize() function above.

Additionally:

  1. The initialize() supports both try/catch and timeouts for the indexing-phase of the web worker.
  2. The worker.onmessage callback is explicitly reset once index-ready is handled; so the web worker is clearly partitioned into two (2) phases: indexing and query/search.
  3. The internal worker.$queue FIFO is never exposed/available to external consumers.
  4. The FIFO promise chain guarantees that no query overlaps will occur; a new query will only be posted when the current query is finished.
  5. You could easily memoize the query results within a internal cache
@petebacondarwin

This comment has been minimized.

Copy link

@petebacondarwin petebacondarwin commented Nov 20, 2014

Looking better and better. Now if it only had some unit tests ....

@ThomasBurleson

This comment has been minimized.

Copy link
Owner Author

@ThomasBurleson ThomasBurleson commented Nov 21, 2014

You sound just like me with the Angular Material team:

  • "Hey Developer, I love those changes... but where are the tests?"
  • "Wow, great fix. Did you write a test for it?"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.