Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * 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;
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement