Reaction

by plus5keen

JavaScript

(function (global) {
    function objectEach (obj, fn) {
        if (!obj) { return; }
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                fn(obj[key], key, obj);
            }
        }
    }
    
    function callFn(fn) {
        if (fn) { fn(); }
    }
    
    function nextTick(fn) {
        setTimeout(fn, 1);
    }

    var debug = {};

    var trigger = (function (setTimeout) {
        var fnTriggerMap = {};
        var pendingTrigger = false;

        function executeTriggers() {
            objectEach(fnTriggerMap, callFn);
            fnTriggerMap = {};
            pendingTrigger = false;
        }

        return function trigger(id, fn) {
            fnTriggerMap[id] = fn;
            if (pendingTrigger) { return; }
            nextTick(executeTriggers);
            pendingTrigger = true;
        };
    }(global.setTimeout));

    function createCounter() {
        var value = 0;
        return function () {
            return value++;
        };
    }

    var currentWatchFn = null;
    var run = (function () {
        var count = createCounter();

        return function run(fn) {
            var id = '_run_' + count();
            nextTick(function () {
                var prevWatchFn = currentWatchFn;
                currentWatchFn = function (fnWatchMap) {
                    fnWatchMap[id] = function () {
                        if (fn) { trigger(id, fn); }
                    };
                };
    
                try {
                    callFn(fn);
                } finally {
                    currentWatchFn = prevWatchFn;
                }
            });

            return function cancel() {
                fn = null;
            };
        };
    }());

    var createReactor = function createReactor() {
        var triggerMap = {};
        var fnWatchMapMap = {};

        return {
            watch: function (name) {
                if (!currentWatchFn) { return; }
                var...