— хочу поделиться своим опытом в работе с воркерами.
Воркеры (Web-workers), это технология, позволяющая запускать изолированные участки кода в отдельном потоке. Код из воркера не тормозит работу графического интерфейса, и выполняется быстрее, чем код на странице, что делает использование воркеров очень привлекательным, для ресурсоёмких расчётов, типа рисования графики или криптографии.
Кто ещё не встречался с этой технологией — здесь можно ознакомится с её основами.
Ниже, описан небольшой «лайфхак», который позволяет уменьшить количество кода, необходимого для вызова функций из воркера, если нужно вызывать больше одной функции.
Обычно, если в нашем воркере всего одна функция, то мы просто пишем:
снаружи(в коде страницы):
var worker = new Worker("myscript.js"); worker.onmessage (event.data){workerСallback(event.data);} function workerСallback(data){ /*do something with data object;*/} worker.postMessage({some:"some"});
внутри(в коде воркера):
onmessage = function (event) {postMessage(mySingleFunction(event.data));}
Пока, всё просто и изящно.
Однако, если в воркер добавить ещё одну функцию, вызываемую извне, то количество кода, обеспечивающего вызов этих функций — возрастёт, и он будет выглядеть уже не так изящно:
снаружи:
function firstFunctionСallback(data){ /*do something with data object;*/} function secondFunctionСallback(data){ } worker.onmessage (msg){ if(msg.data.callback == "firstFunctionСallback"){ firstFunctionСallback(msg.data.result); } if(msg.data.callback == "secondFunctionСallback"){ firstFunctionСallback(msg.data.result); } } worker.postMessage({functionName: "firstFunction", data: data);
внутри:
onmessage = function (event) { var functionName = event.data.functionName; if(functionName == "firstFinction"){ postMessage({callback: "firstFunctionСallback", result: firstFinction(event.data.data)}); } if(functionName == "secondFunction"){ postMessage({callback: "secondFunctionСallback", result: secondFunction(event.data.data)}); } ... }
При таком подходе, нет возможности использовать в качестве коллбэков анонимные функции, и при каждом добавлении новой функции в воркер, нужно каждый раз, снаружи и внутри воркера, писать некоторое количество дополнительного кода, обеспечивающего вызов этой функции.
Чтобы избежать этого — воркер можно обернуть в объект, который будет выполнять эту работу.
Назовём такой объект, соответственно, Performer, и разместим его во внешнем коде:
function Performer(scriptSource) { var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0; this.perform = function(functionName, params, callback) { callbacks["request_" + (++nextRequestId)] = callback; worker.postMessage( {functionName: functionName, params: params, requestId: nextRequestId} ); } worker.onmessage = function(msg) { callbacks["request_" + msg.data.requestId](msg.data.result); delete callbacks["request_" + msg.data.requestId]; } }
Во внутреннем коде воркера — изменим обработчик внешних сообщений:
onmessage = function (event) { 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}); }
Теперь, можно добавлять в воркер любые функции, и вызывать их извне, без написания вспомогательного кода, а также, использовать анонимные функции в коллбэках:
var performer = new Performer("myscript.js"); performer.perform("firstFunction", {some: "some"}, function(result){console.log("result1="+result);}); performer.perform("secondFunction", {some: "some"}, function(result){console.log("result2="+result);});
Ели воркер размещён не в отдельном файле скрипта, а встроен в страницу,
то в коде перформера должны быть учтены кроссбраузерные различия.
С их учётом, часть перформера, которая отвечает за инициализацию воркера, будет выглядеть так:
function Performer(scriptText) { var worker = null; try {// Firefox var Url = window.webkitURL || window.URL; worker = new Worker(Url.createObjectURL(new Blob([ scriptText ]))); } catch (browserNotSupportWindowUrl) { try {// Chrome worker = new Worker('data:application/javascript,' + encodeURIComponent(scriptText)); } catch (browserNotSupportWorkers) {// Opera eval(scriptText); worker = { postMessage : function(data) { var workerFunction = eval(data.functionName); worker.onmessage({ data : { result : workerFunction(data.params), requestId : data.requestId } }); } }; } } ... }
а создание, соответственно, так:
var performer = new Performer($('#myscript').text());
Таким образом, даже в браузерах не поддерживающих воркеры, код воркера всё равно будет выполняться, просто медленнее.
UPD: благодарю Неизвестного Благодетеля за инвайт).
ссылка на оригинал статьи http://habrahabr.ru/post/207890/
Добавить комментарий