marko/components/update-manager.js
2017-02-20 16:30:16 -07:00

132 lines
4.0 KiB
JavaScript

'use strict';
var updatesScheduled = false;
var batchStack = []; // A stack of batched updates
var unbatchedQueue = []; // Used for scheduled batched updates
var win = window;
var setImmediate = win.setImmediate;
if (!setImmediate) {
if (win.postMessage) {
var queue = [];
var messageName = 'si';
win.addEventListener('message', function (event) {
var source = event.source;
if (source == win || !source && event.data === messageName) {
event.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
setImmediate = function(fn) {
queue.push(fn);
win.postMessage(messageName, '*');
};
} else {
setImmediate = setTimeout;
}
}
/**
* This function is called when we schedule the update of "unbatched"
* updates to components.
*/
function updateUnbatchedComponents() {
if (unbatchedQueue.length) {
try {
updateComponents(unbatchedQueue);
} finally {
// Reset the flag now that this scheduled batch update
// is complete so that we can later schedule another
// batched update if needed
updatesScheduled = false;
}
}
}
function scheduleUpdates() {
if (updatesScheduled) {
// We have already scheduled a batched update for the
// process.nextTick so nothing to do
return;
}
updatesScheduled = true;
setImmediate(updateUnbatchedComponents);
}
function updateComponents(queue) {
// Loop over the components in the queue and update them.
// NOTE: It is okay if the queue grows during the iteration
// since we will still get to them at the end
for (var i=0; i<queue.length; i++) {
var component = queue[i];
component.update(); // Do the actual component update
}
// Clear out the queue by setting the length to zero
queue.length = 0;
}
function batchUpdate(func) {
// If the batched update stack is empty then this
// is the outer batched update. After the outer
// batched update completes we invoke the "afterUpdate"
// event listeners.
var batch = {
$__queue: null
};
batchStack.push(batch);
try {
func();
} finally {
try {
// Update all of the components that where queued up
// in this batch (if any)
if (batch.$__queue) {
updateComponents(batch.$__queue);
}
} finally {
// Now that we have completed the update of all the components
// in this batch we need to remove it off the top of the stack
batchStack.length--;
}
}
}
function queueComponentUpdate(component) {
var batchStackLen = batchStack.length;
if (batchStackLen) {
// When a batch update is started we push a new batch on to a stack.
// If the stack has a non-zero length then we know that a batch has
// been started so we can just queue the component on the top batch. When
// the batch is ended this component will be updated.
var batch = batchStack[batchStackLen-1];
// We default the batch queue to null to avoid creating an Array instance
// unnecessarily. If it is null then we create a new Array, otherwise
// we push it onto the existing Array queue
if (batch.$__queue) {
batch.$__queue.push(component);
} else {
batch.$__queue = [component];
}
} else {
// We are not within a batched update. We need to schedule a batch update
// for the process.nextTick (if that hasn't been done already) and we will
// add the component to the unbatched queued
scheduleUpdates();
unbatchedQueue.push(component);
}
}
exports.$__queueComponentUpdate = queueComponentUpdate;
exports.$__batchUpdate = batchUpdate;