
WebWorker - Создание потока выполнения асинхронной задачи (обновлено)
[Обновлено 21 апреля 2021 г.] Я опубликовал новую служебную веб-утилиту с улучшенным способом выполнения функций в рабочем потоке. Ссылка на репо приведена ниже. Пожалуйста, проверьте это.
Современные браузеры день ото дня становятся все более мощными. С приходом компьютеров и мобильных устройств, ориентированных на процессор, ваши браузеры могут обрабатывать тяжелую обработку, графику и анимацию, вы называете это. Но… эти возможности имеют свою цену. Пока движки браузера заняты обработкой тяжелых нагрузок, ваш интерфейс может зависнуть. Это приводит к плохому пользовательскому опыту. Вот где на сцену выходят WebWorkers.
WebWorkers существуют уже некоторое время. Они были разработаны для обеспечения неблокирующего потока выполнения задач. Этот поток можно использовать для вычисления дорогостоящих и трудоемких задач процессора, не затрагивая основной поток браузера. Чтобы узнать больше о веб-воркерах и их различных приложениях, вы можете обратиться к этой статье https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
Я работал над небольшим фреймворком, который помогал бы организованно запускать задачи на веб-воркерах. Мне нужно было решить следующие проблемы:
1. Простой способ создать и выполнить задачу в рабочем потоке веб-сайта
2. Связь между основным потоком браузера и рабочим потоком веб-сайта
3. Доступ ко всем задачам, выполняемым в веб-рабочем потоке
Теперь, прежде чем мы начнем кодировать, нужно запомнить несколько моментов.
- Рабочий веб-поток полностью изолирован от основного потока. Он не может получить доступ к окну, документу и домену основного потока.
- Он предоставляет API для связи между двумя потоками, но можно обмениваться только сериализуемыми данными. Это означает, что мы не можем передавать функции как обратные вызовы.
- Web Worker предоставляет API postMessage () для обмена сообщениями между основным и рабочим потоком Web.
Начнем с простого примера сложения двух чисел в веб-воркерах.
------ GlobalWorker.js --------
onmessage = function(e){
var num1 = e.data[0];
var num2 = e.data[1];
var result = num1 + num2;
console.log(result)
}
------ Main.js ------
Globals.worker = new Worker('path/GlobalWorker.js');
Globals.worker.postMessage([5, 6])
GlobalWorker.js - это наш js-файл, который будет выполняться в потоке WebWorker.
Main.js будет выполняться в основном потоке браузера.
Задача 1. Создание глобального объекта WebWorker для добавления новых задач в рабочий поток.
------GlobalWorker.js-------
Worker = {
init: function(){
onmessage = this.onMessage;
this.tasks = {}
},
addTask: function(task){
this.tasks[task.name] = task
},
onMessage: function(e){
var self = Worker
var name = e.data.name,
args = e.data.args
if(self.tasks[name] && self.tasks[name].main){
self.tasks[name].main(args)
}
}
}
function Task(conf){
var self = this
if(!('name' in conf && 'main' in conf)){
throw "Task must have a Name and Main method";
}
Object.keys(conf).forEach(function(k){
self[k] = conf[k];
})
return self;
}
--------Main.js--------
Globals.Worker = {
init: function(){
this.worker = new Worker('path/worker.js');
}
executeWorkerTask: function(taskName, taskArgs){
var args = {
name: taskName,
args: taskArgs
}
this.worker.postMessage(args)
}
}
Конструкторы задач будут создавать простые объекты задач. У всех задач должно быть имя и свойство основного метода. Свойство «name» действует как идентификатор, и «основной» метод задачи будет выполнен, когда задача будет зарегистрирована из Main.js.
Теперь, когда основная структура готова, давайте создадим простую задачу для сложения 2 чисел.
-----GlobalWorker.js------
var add_task = new Task({
name: 'add',
main: function(a, b){
console.log(a + b)
});
Worker.addTask(add_task)
------Main.js------
Globals.Worker.executeWorkerTask('add', [5,6])
Задача 2: настройка связи между двумя потоками
Теперь, поскольку мы хотим иметь возможность выполнять несколько задач в рабочем потоке сети, нам понадобится механизм, позволяющий задачам в GlobalWorker.js взаимодействовать с Main.js. Добавим карту слушателей в Main.js. Когда postMessage () вызывается из рабочего потока Web, эти слушатели будут выполнены. Вот код
------GlobalWorker.js--------
Worker = {
init: function(){
onmessage = this.onMessage;
this.tasks = {}
},
addTask: function(task){
this.tasks[task.name] = task
},
onMessage: function(e){
var name = e.data.name,
args = e.data.args
if(this.tasks[name] && this.tasks[name].main){
this.tasks[name].main(args)
}
}
}
function Task(conf){
var self = this
if(!('name' in conf && 'main' in conf)){
throw "Task must have a Name and Main method";
}
Object.keys(conf).forEach(function(k){
self[k] = conf[k];
})
self.postMessage = function(data){
postMessage({
name: self.name,
data: data
})
}
return self;
}
var add_task = new Task({
name: 'add',
main: function(a, b){
this.postMessage(a+b)
});
})
Worker.addTask(add_task)
--------Main.js--------
Globals.Worker = {
init: function(){
this.worker = new Worker('path/worker.js');
this.worker.onmessage = this.messageListener
this.listeners = {}
}
executeWorkerTask: function(taskName, taskArgs, taskListener){
var args = {
name: taskName,
args: taskArgs
}
this.worker.postMessage(args)
if(taskListener){
this.listeners[taskName] = taskListener
}
},
messageListener: function(e){
var name = e.data.name
var data = e.data.data
if(this.listeners[name]){
this.listeners[name](data)
}
}
}
Globals.Worker.executeWorkerTask('add', [5,6], function(result){
console.log(result)
})
Worker.addTask(add_task), который добавит Задачу, которую мы хотим выполнить, в GlobalWorker.js. Globals.Worker.executeWorkerTask(name, args, listenerFunc) выполнит их из Main.js и присоединит функцию слушателя. this.postMessage(a+b) выполнит прослушиватель для задачи «добавить» из списка this.listeners.
Мы можем просто добавлять новые задачи и выполнять их, как показано ниже.
------ GlobalWorker.js-------
var sub_task = new Task({
name: 'sub',
main: function(a, b){
this.postMessage(a-b)
});
})
var multiply_task = new Task({
name: 'multiply',
main: function(a, b){
this.postMessage(a*b)
});
})
Worker.addTask(sub_task)
Worker.addTask(multiply_task)
-------- Main.js --------
Globals.Worker.executeWorkerTask('add', [5,6], function(result){
console.log(result)
})
Globals.Worker.executeWorkerTask('sub', [5,6], function(result){
console.log(result)
})
Globals.Worker.executeWorkerTask('multiply', [5,6], function(result){
console.log(result)
})
Удачного кодирования !!
Локеш Патрабе