Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Асинхронный вызов функций (https://javascript.ru/forum/misc/45887-asinkhronnyjj-vyzov-funkcijj.html)

Octane 19.03.2014 17:21

Асинхронный вызов функций
 
Посоветуйте, какой из вариантов реализации setImmediate выбрать для асинхронного вызова функций?

Первый вариант:
var callAsync = function () {

	var fakeNode = document.createElement("img"), storage = {}, uid = 0;
	fakeNode.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
	fakeNode.style.cssText = "position: absolute; top: -9999px; left: -9999px; width: 0; height: 0;";
	document.body.appendChild(fakeNode);
	fakeNode.addEventListener("load", function (event) {
		var key = event.key, data = storage[key];
		data.callback.call(data.thisObj, data.data);
		delete storage[key];
	});

	return function (callback, thisObj, data) {
		var key = "call" + uid++;
		storage[key] = {
			data: data,
			thisObj: thisObj,
			callback: callback
		};
		var event = document.createEvent("HTMLEvents");
		event.initEvent("load", false, false);
		event.key = key;
		fakeNode.dispatchEvent(event);
	};

}();

callAsync(function () {
	throw Error("test");
});
alert("done");
В этом варианте, мне не нравится, что лишний <img> будет все время находится на странице, попадать в коллекции или мешать какому-нибудь nth-child. А если кому-нибудь вздумается скрипт в <head> засунуть, то придется еще и DOMContentLoaded добавлять для всего, что будет использовать callAsync.


Второй вариант:
var message = "ServiceMessage", storage = {}, uid = 0;

addEventListener("message", function (event) {
	var key = event.data, data;
	if (typeof key == "string" && key.indexOf(message) == 0) {
		data = storage[key];
		data.callback.call(data.thisObj, data.data);
		delete storage[key];
	}
});

function callAsync(callback, thisObj, data) {
	var key = message + uid++;
	storage[key] = {
		data: data,
		thisObj: thisObj,
		callback: callback

	};
	postMessage(key, "*");
}

callAsync(function () {
	throw Error("test");
});
alert("done");
В этом варианте боюсь, что постоянный флуд в message кому-нибудь навредит.



Третий вариант с использованием setTimeout не рассматриваю, так как не устраивает скорость выполнения.

nerv_ 19.03.2014 18:01

Цитата:

Сообщение от Octane
Третий вариант с использованием setTimeout не рассматриваю, так как не устраивает скорость выполнения.

поясни

http://stackoverflow.com/a/9516967

Octane 19.03.2014 18:39

Цитата:

Сообщение от nerv_
поясни

у setTimeout есть минимальная задержка 3-5 мс, передавать 0 бесполезно

nerv_ 19.03.2014 19:00

Цитата:

Сообщение от Octane
у setTimeout есть минимальная задержка 3-5 мс, передавать 0 бесполезно

это понятно :)
Если функция вызывается асинхронно, то время, когда именно она будет вызвана может варьироваться и зависит от ряда факторов. О какой скорости может идти речь в этом случае, я не понимаю :)

В первом варианте заменил alert() на console.log(), в хроме получилось синхронно.

Octane 19.03.2014 19:04

Тесты на скорость выполнения:

var intervals = [], i = 100, t1 = Date.now(), t2;

setTimeout(function () {

	t2 = Date.now();

	intervals.push(t2 - t1);

	t1 = t2;

	if (i--) {
		setTimeout(arguments.callee, 0);
	}
	else {
		alert(intervals);
	}

}, 0);



var message = "ServiceMessage", storage = {}, uid = 0;

addEventListener("message", function (event) {
	var key = event.data, data;
	if (typeof key == "string" && key.indexOf(message) == 0) {
		data = storage[key];
		data.callback.call(data.thisObj, data.data);
		delete storage[key];
	}
});

function callAsync(callback, thisObj, data) {
	var key = message + uid++;
	storage[key] = {
		data: data,
		thisObj: thisObj,
		callback: callback

	};
	postMessage(key, "*");
}

var intervals = [], i = 100, t1 = Date.now(), t2;

callAsync(function () {

	t2 = Date.now();

	intervals.push(t2 - t1);

	t1 = t2;

	if (i--) {
		callAsync(arguments.callee);
	}
	else {
		alert(intervals);
	}

});

nerv_ 29.03.2014 01:17

Еще ссылки

setImmediate
The case for setImmediate()

Octane 30.03.2014 04:11

Есть идеи, почему у первого варианта в 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;

};
а этот вариант нормально работает


Часовой пояс GMT +3, время: 09:40.