Created
July 29, 2016 13:48
-
-
Save fidel-karsto/5e9a5f646234dd5eefab21988f1e7196 to your computer and use it in GitHub Desktop.
ObservableArray.getAsyncTwin() for Knockout
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
/** | |
* Adds a `getAsyncTwin()` method to the prototype of Knockout's ObservableArray | |
* returning a new ObservableArray that will be a continuous non-instant copy of the original. | |
* Whenever the original ObservableArray changes, the copy will be populated | |
* in chunks of a given size and with a given delay between the chunks until it is identical to the original. | |
* | |
* Intended to allow rendering the first (visible) part of huge lists faster before all the rest. | |
* | |
* Example: | |
* | |
* var hugeList = ko.observableArray(someHugeArray), | |
* hugeListToRender = hugeList.getAsyncTwin({ | |
* chunkSize: 100, | |
* chunkDelay: 1 | |
* }); | |
* | |
* <ul data-bind="foreach: hugeListToRender">..</ul> | |
*/ | |
define(['knockout', 'util', 'LOG'], function(ko, util, LOG) { | |
'use strict'; | |
/** | |
* @param opts (Object) | |
* - chunkSize (number|function) number of items to copy per update interval OR a function to determine | |
* the number based on the new total size, | |
* e.g. function(total){ return (total <= 100) ? 20 : 10 } | |
* - chunkDelay (number|function) delay in millis between the chunks OR a function to determine the delay | |
* based on the new total size, | |
* e.g. function(total){ return (total <= 100) ? 20 : 10 } | |
*/ | |
ko.observableArray.fn.getAsyncTwin = function(opts) { | |
util.assertObject(opts, 'Invalid options object for observableArray.getAsyncTwin'); | |
if (typeof opts.chunkSize === 'number') { | |
util.assertNumberInRange(opts.chunkSize, 1, 1000000, 'Illegal chunkSize number for observableArray.getAsyncTwin'); | |
} else { | |
util.assertFunction(opts.chunkSize, 'Invalid chunkSize option for observableArray.getAsyncTwin'); | |
} | |
if (typeof opts.chunkDelay === 'number') { | |
util.assertNumberInRange(opts.chunkDelay, 1, 60000, 'Illegal chunkDelay number for observableArray.getAsyncTwin'); | |
} else { | |
util.assertFunction(opts.chunkDelay, 'Invalid chunkDelay option for observableArray.getAsyncTwin'); | |
} | |
var sourceObsArray = this, | |
twinObsArray = ko.observableArray(), | |
chunkSize = -1, // determined on update start | |
chunkDelay = -1, // determined on update start | |
timer = null, | |
continueUpdate = function(clearArray) { | |
if (timer) { | |
clearTimeout(timer); | |
timer = 0; | |
} | |
if (clearArray) { | |
twinObsArray().length = 0; | |
} | |
var sourceArray = sourceObsArray(), | |
twinArray = twinObsArray(), | |
firstItemIndex = twinArray.length, | |
lastItemIndex = Math.min(firstItemIndex + chunkSize - 1, sourceArray.length - 1); | |
if (firstItemIndex <= lastItemIndex) { | |
LOG.debug('Adding items %s to %s', firstItemIndex, lastItemIndex); | |
for (var i = firstItemIndex; i<= lastItemIndex; i++) { | |
twinArray[i] = sourceArray[i]; | |
} | |
if (twinArray.length < sourceArray.length) { | |
LOG.debug('Scheduling next chunk in %s millis', chunkDelay); | |
timer = setTimeout(continueUpdate, chunkDelay); | |
} else { | |
LOG.debug('asyncTwin complete [total=%s]', twinArray.length); | |
} | |
} else { | |
LOG.debug('Empty chunk for asyncTwin#continueUpdate'); | |
} | |
twinObsArray.valueHasMutated(); | |
}, | |
startUpdate = function() { | |
if (timer) { | |
clearTimeout(timer); | |
timer = 0; | |
} | |
var newSize = sourceObsArray().length; | |
chunkSize = (typeof opts.chunkSize === 'function') ? opts.chunkSize(newSize) : opts.chunkSize; | |
chunkDelay = (typeof opts.chunkDelay === 'function') ? opts.chunkDelay(newSize) : opts.chunkDelay; | |
LOG.debug('asyncTwin#startUpdate [total=%s|chunkSize=%s|chunkDelay=%s]', newSize, chunkSize, chunkDelay); | |
continueUpdate(true); | |
}; | |
sourceObsArray.subscribe(startUpdate); | |
startUpdate(); | |
return twinObsArray; | |
}; | |
return ko; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment