Test TinySafeEvents
by Dmitry Astafyev
JavaScript
"use strict"
var SafeEvents = (function() {
var events = new Map();
var callstack = [];
bind('onloop', (stack) => console && console.warn && console.warn("Loop detected! " + JSON.stringify(stack)));
function bind(event, handler) {
var list = events.get(event);
if (!list) events.set(event, list = []);
list.push(handler);
}
function unbind(event, handler) {
var list = events.get(event);
if (!list) return;
var index = list.indexOf(handler)
if (index >= 0) return list.splice(index, 1);
}
function trigger(event, ...args) {
var n = callstack.push(event);
try {
if (callstack.indexOf(event) !== n - 1 && event !== 'onloop') trigger('onloop', callstack.slice());
var list = events.get(event);
if (list) list.forEach(handler => handler(...args));
} finally {
callstack.pop();
}
}
function safely(callback) {
var saved_stack = callstack.slice();
saved_stack.push("(async)");
return function() {
var inner_saved_stack = callstack;
callstack = saved_stack;
try {
var result = callback.apply(this, arguments);
} finally {
callstack = inner_saved_stack;
}
return result;
}
}
var global = Function('return this')();
if (global) {
['setTimeout', 'setInterval', 'setImmediate', 'requestAnimationFrame'].forEach(name => {
if (typeof global[name] === 'function') {
var impl = global[name];
global[name] = function(callback, ...args) {
return impl(safely(callback), ...args)
}
}
})
}
return {
bind: bind,
unbind: unbind,
trigger: trigger,
safely: safely,
};
}());
/*
* Test A. Classic loop.
* Events ordering: A -> B -> C -> A -> loop.
*
* Console: Uncaught Error: Event [A] called itself. Full chain: A, B, C
*/
SafeEvents.bind('A', function() {
SafeEvents.trigger('B');
});
SafeEvents.bind('B', function() {
...