Pull to refresh

Comments 26

Оно не должно работать быстрее, потому что под капотом у воркеров живёт всё тот же js движок, который исполняется в тот же один поток, но на другом ядре. Единственное что может сказываться — области видимости. Поиск переменной в контексте window является на самом деле не дешевым удовольствием, о чём часто забывают.
Возможно, что быстрее будет не всегда, но генерация пары ключей 2048 бит в Фаерфоксе,
в частности, происходит, примерно, в 7 раз быстрее.
На самом деле — нет(.
Разница была из-за включённого фаербага, который тормозил код вне воркера.
С отключенным фаербагом — разницы нет.

Приношу извинения.
Пытался использовать воркеры, но уперся в передачу ему параметров.
У меня через вебсокеты приходит большой объем данных в виде массива байт, и я хочу в воркере его разобрать в массив(прочитать там int'ы, строки, байты и прочее) и вернуть результат основному потоку.
Но вот как передать большой массив в воркер не понятно, так как просто так туда объекты не передашь.
Может кто-нибудь разъяснить, как все таки лучше передавать туда данные?
Это будет двойной работой, так как при передаче в воркер, ваш массив атоматом сериализуется в JSON, а там десериализуется…

С другой стороны в воркерах доступен AJAX может стоит получать данные уже в нем парсить и т.д., а результат возвращать…
Может кто-то подсказать как сделать отрисовку в канвасе в отдельном потоке?
Ну и нафик эти воркеры не нужны тогда.
Если подробней — в воркерах нельзя обращаться к DOM модели, и вообще нету доступа к объекту window. Так что…
Ну хоть у кого-то есть реальный опыт применения воркеров и получения значительного или хоть какого-то повышения производительности?
Прошу прощения, на самом деле, это я Фома невнимательный.
Проверял разницу, раньше, только при включённом фаербаге.
Сейчас, при проверке этого примера, фаерфокс упал, а когда перезагрузился — фаербаг был отключен,
и результаты стали почти одинаковые.
Мало того, иногда, без воркера — работает быстрее!

Теперь и я присоединяюсь к Вашему вопросу, насчёт повышения производительности)
С RSA почти хороший пример, вот только я не вижу преимуществ перед его генерацией на сервере.
В данном случае, RSA используется в таком сценарии.
Поэтому, ключ должен генерироваться на клиенте.
Ну, так-то workers придуманы, чтобы не грузить GUI поток. Вот и всё. Это как в Android (образно). Все тяжеловесные задачи выполняются в фоне — и в результате картинка не прыгает, отклик на мышь нормальный. А повышения производительности не добиться. Что быстрее js или js?
Для вызовов функций внутри воркера извне, очень подошли бы Proxy.
Да, точно. Можно обернуть этот перформер в прокси:

function MyWorkerProxy(scriptUrl){

    var performer = new Performer(scriptUrl);    
        
    this.firstFunction = function(params, callback){
      performer.perform("firstFunction", params, callback);	
    }            
    ...    
}

и вызывать внутренние функции красиво:

var myWorkerProxy = new MyWorkerProxy("myscript.js");

myWorkerProxy.firstFunction({some: "some"}, function(result){console.log("result1="+result);});
Я имею в виду ES Harmony Proxy. Надеюсь, что вебкит включат его по умолчанию в ближайшем будущем.
Может можно автоматическое определение функций сделать? Ну, как–то так:

во внешнем коде
function Performer(scriptSource) {
    var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0;	
    var perform = function(functionName, params, callback) {
        callbacks["request_" + (++nextRequestId)] = callback;
        worker.postMessage(
         {functionName: functionName, params: params, requestId: nextRequestId}
        );
    }

    var self = this;
    worker.onmessage = function(msg) {
        if (msg.data.__methods__) {   // получили список функций, делаем их методами Performer'a
            var methods = msg.data.__methods__;
            var appendMethod = function (name) {
                self[name] = function (params, callback) {
                    perform(name, params, callback);
                }
            };
            for (var i = 0; i < methods.length; ++i) { 
                appendMethod(method[i]);
            }
        }
        else {
            callbacks["request_" + msg.data.requestId](msg.data.result);
            delete callbacks["request_" + msg.data.requestId];
        }
    }

    perform('__init__');
}


во внутреннем коде:
onmessage = function (event) {
    if (event.data.functionName === '__init__') {
        var methods = [    // может и тут как–то можно автоматизировать?
            'firstFunction',
            'secondFunction'
            ...
        ];
        postMessage({__methods__: methods }); 
    }
    else {
        var requestId = event.data.requestId;
        var workerFunction = eval(event.data.functionName);
        var params = event.data.params;
        var result =  workerFunction(params);
        postMessage({result: result, requestId: requestId}); 
    }
}
Упс, промахнулся. Смотрите комент ниже.
О, так вообще классно получается! (если ещё добавить обработку ошибок)
Теперь можно с полным правом переименовать Performer в WorkerProxy:

function WorkerProxy(scriptSource) {

    var self = this;
	
	var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0;	
	
	this.perform = function(functionName, params, successCallback, errorCallback) {
		callbacks["request_" + (++nextRequestId)] = 
		{successCallback: successCallback, errorCallback: errorCallback};
		worker.postMessage({functionName: functionName, params: params, requestId: nextRequestId});
	}
			
	worker.onmessage = function(msg) {
		if (msg.data.__methods__) {   // получили список функций, делаем их методами WorkerProxy
			var methods = msg.data.__methods__;
	        for (var i = 0; i < methods.length; i++) { 	       
	        	eval('self["'+methods[i]+'"] = function (params, successCallback, errorCallback) {'
	            	+'self.perform("'+methods[i]+'", params, successCallback, errorCallback);}');	                    
	        }
	        return;
	    }
        if(msg.data.error){
          if( callbacks["request_" + msg.data.requestId].errorCallback){
        	  callbacks["request_" + msg.data.requestId].errorCallback(msg.data.error);
          }else{
        	  alert(msg.data.error);
          }
        }else{	
    	  callbacks["request_" + msg.data.requestId].successCallback(msg.data.result);
        }
        delete callbacks["request_" + msg.data.requestId];	   
	}	
	
	this.perform('__init__');
}


а в воркере будет так:

    onmessage = function (event) {
	    if (event.data.functionName === '__init__') {
	        var methods = [];
	        for(var f in self){methods.push(f);}
	        postMessage({__methods__: methods }); 
	        return;
	    }
    	var requestId = event.data.requestId;
	    try{		       
	        var workerFunction = eval(event.data.functionName);
	        var params = event.data.params;
	        var result =  workerFunction(params);
	        postMessage({result: result, requestId: requestId});	        
	    }catch(ex){
	    	postMessage({error: ex + ", functionName="+event.data.functionName, requestId: requestId});
	    }  	    
	}  


и вызов:

var myWorker = new WorkerProxy("myscript");
myWorker.firstFunction({some:"some"}, function(result){console.log("result1="+result);});
for(var f in self)

немножко настораживает — сюда попадёт вообще всё что только есть в self (честно говоря, не знаю что он содержит, но подозреваю, что немало из того, что имеется в window). Безопаснее сделать реализацию методов worker'a в объекте:
function MyWorker() {
    this.firstFunction = function (params) {
        // implementation 
    };
    this.firstFunction = function (params) {
        // implementation 
    };
    // другие публичные методы, приватные методы и данные объявляются через var privateVariable, privateFunction
}

и тогда
var myWorker = new MyWorker();

onmessage = function (event) {
        if (event.data.functionName === '__init__') {
            var methods = [];
            for(var f in myWorker) {
                if (myWorker.hasOwnProperty(f) && typeof f === 'function') {  // вторая проверка, возможно, избыточна
                    methods.push(f);
                }
            }
            postMessage({__methods__: methods }); 
            return;
        }

        var requestId = event.data.requestId;
        try{		       
            var params = event.data.params;
            var result = myWorker[event.data.functionName] (params);
            postMessage({result: result, requestId: requestId});	        
        }catch(ex){
        	postMessage({error: ex + ", functionName="+event.data.functionName, requestId: requestId});
        }  	    
    }  


alert(msg.data.error);

дело вкуса, наверное, но console.log вместо alert часто оказывается удобнее.

Да, очень неплохой удобный wrapper получается.
Действительно, лишнего много тянется (и есть теоретический риск по ошибке вызвать что-то не то).
Тогда наверное, лучше, чем дописывать что-то в воркер,
сделать всё во внешнем коде и передать список публичных методов в параметрах:

function WorkerProxy(scriptSource, publicWorkerFunctions) {
    var self = this;
    var methods = (typeof(publicWorkerFunctions) == "string") ? publicWorkerFunctions.split(",") : publicWorkerFunctions;
    for (var i = 0; i < methods.length; i++) { 	       
	        	eval('self["'+methods[i]+'"] = function (params, successCallback, errorCallback) {'
	            	+'self.perform("'+methods[i]+'", params, successCallback, errorCallback);}');	                    
	}
	
	var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0;	
		
	this.perform = function(functionName, params, successCallback, errorCallback) {
		callbacks["request_" + (++nextRequestId)] = 
		{successCallback: successCallback, errorCallback: errorCallback};
		worker.postMessage({functionName: functionName, params: params, requestId: nextRequestId});
	}
			
	worker.onmessage = function(msg) {		
       if(msg.data.error){
          if( callbacks["request_" + msg.data.requestId].errorCallback){
        	  callbacks["request_" + msg.data.requestId].errorCallback(msg.data.error);
          }else{
        	  alert(msg.data.error);
          }
        }else{	
    	  callbacks["request_" + msg.data.requestId].successCallback(msg.data.result);
        }
        delete callbacks["request_" + msg.data.requestId];	   
	}	
}


внутри:

    onmessage = function (event) {
    	var requestId = event.data.requestId;
	    try{		       
	        var workerFunction = eval(event.data.functionName);
	        var params = event.data.params;
	        var result =  workerFunction(params);
	        postMessage({result: result, requestId: requestId});	        
	    }catch(ex){
	    	postMessage({error: ex + ", functionName="+event.data.functionName, requestId: requestId});
	    }  	    
	}  


вызов:

var myWorker = new WorkerProxy("myscript", "firstFunction,secondFunction");
myWorker.firstFunction({some:"some"}, function(result){console.log("result1="+result);});


Чтобы console.log работало — нужно же Фаербаг иметь).
Нет, так плохо.

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

Всё-таки, performer.perform(«firstFunction», {some: «some»}, function(result){;}); — оптимальный вариант.
Sign up to leave a comment.

Articles