Есть идеи, почему у первого варианта в IE8 длина стека вызовов всего 5?
/**
 * setImmediate polyfill
 */
window.setImmediate || new function () {
	var id = 0, storage = {}, message = "setImmediatePolyfillMessage";
	function fastApply(args) {
		var func = args[0];
		switch (args.length) {
			case 1: return func();
			case 2: return func(args[1]);
			case 3: return func(args[1], args[2]);
		}
		return func.apply(window, Array.slice(args, 1));
	}
	function callback(event) {
		var key, data;
		event = event || window.event;
		key = event.data;
		if (typeof key == "string" && key.startsWith(message)) {
			data = storage[key];
			if (data) {
				fastApply(data);
				delete storage[key];
			}
		}
	}
	function setImmediate() {
		var key = message + ++id;
		storage[key] = arguments;
		postMessage(key, "*");
		return id;
	}
	function clearImmediate(id) {
		delete storage[message + id];
	}
	addEventListener("message", callback, false);
	window.setImmediate = setImmediate;
	window.clearImmediate = setImmediate;
};
пробовал arguments преобразовывать в массив, не помогает, 6-й рекурсиный вызов приводит к stack overflow
/**
 * IE8 setImmediate polyfill
 */
document instanceof Object || new function () {
	var root = document.documentElement, fakeScript = document.createElement("script");
	//todo code reuse
	function fastApply(args) {
		var func = args[0];
		switch (args.length) {
			case 1: return func();
			case 2: return func(args[1]);
			case 3: return func(args[1], args[2]);
		}
		return func.apply(window, Array.slice(args, 1));
	}
	function setImmediate () {
		var args = arguments, tmpScript = fakeScript.cloneNode(false);
		tmpScript.onreadystatechange = function () {
			fastApply(args);
			tmpScript.onreadystatechange = null;
			root.removeChild(tmpScript);
			tmpScript = null;
		};
		root.appendChild(tmpScript);
		return 0;
	}
	function clearImmediate() {}
	window.setImmediate = setImmediate;
	window.clearImmediate = setImmediate;
};
а этот вариант нормально работает