├── src └── overscore.coffee └── readme.md /src/overscore.coffee: -------------------------------------------------------------------------------- 1 | class Overscore 2 | constructor: (array) -> 3 | @utilisation = 75 4 | 5 | # todo - replace with a better clone function 6 | @output = for input in array 7 | input 8 | 9 | @operations = [] 10 | @operation = null 11 | 12 | @interval = setInterval(@_process, 100) 13 | 14 | this 15 | 16 | _process: => 17 | tzero = (new Date).getTime() 18 | 19 | if @operation == null 20 | if @operations.length > 0 21 | @operation = @operations.shift() 22 | @input = for input in @output 23 | input 24 | @output = [] 25 | else 26 | @_finally() 27 | 28 | if @operation 29 | while ((new Date).getTime() - tzero < @utilisation) and (@input.length > 0) 30 | input = @input.pop() 31 | @operation(input) 32 | 33 | if @input.length == 0 34 | @operation = null 35 | 36 | 37 | _finally: -> 38 | clearInterval(@interval) 39 | 40 | if @_finalCallback 41 | @_finalCallback(@output) 42 | 43 | map: (func) -> 44 | @operations.push( (input) => 45 | @output.push(func(input)) 46 | ) 47 | this 48 | 49 | each: (func) -> 50 | @operations.push( (input) => 51 | func(input) 52 | ) 53 | this 54 | 55 | select: (func) -> 56 | @operations.push( (input) => 57 | if func(input) 58 | @output.push(input) 59 | ) 60 | this 61 | 62 | then: (func) -> 63 | @_finalCallback = func 64 | this 65 | 66 | @Overscore = Overscore 67 | # 68 | # window.$O = (array) -> 69 | # new Overscore array -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Overscore 2 | 3 | Non-blocking underscore.js functions. This library lets you run javascript-intensive functions in the background, without locking up the browser, or making the dom update slowly. It uses setInterval under the hood to ensure that the javascript runtime never uses more than 75% of the browsers CPU time. 4 | 5 | ## Problem 6 | 7 | I encountered this problem when writing the [Rankers App](http://itunes.apple.com/nz/app/rankers-app/id454894632?mt=8). I wrote my own geo-marker cluster that used underscore.js. The only problem is that the clusterer iterates over 3000 points, and doing this in one block of code made the map freeze up, so you'd be panning around, and then the map would freeze while I calculated the new points. This made the UI seem really sucky, so I created Overscore. 8 | 9 | ## Solution 10 | 11 | Instead of iterating over the entire input array in one loop, use a regular `100ms` interval that iterates over a chunk of the array, then yields when it has used more than 75ms of browser time. From my own testing, this seems to allow the browser to stay responsive. 12 | 13 | ## API 14 | 15 | I've gone for a chaining api. The only substantial difference from the underscore.js api is the `then` method. This is used for your final logic step. So for example: 16 | 17 | new Overscore(inputs).select( 18 | function(i){ return i.match(/beach/) } 19 | ).then( 20 | function(outputs){ console.log("found " + outputs.length + " beaches")} 21 | ); 22 | 23 | This, the very first release, contains only `map` and `select`. 24 | 25 | ## Future 26 | 27 | * Maybe add webworker support? 28 | * Less duplication of input and output arrays for more speed 29 | * Tests --------------------------------------------------------------------------------