Книга: JavaScript. Подробное руководство, 6-е издание

22.4.3. Примеры использования фоновых потоков

22.4.3. Примеры использования фоновых потоков

Завершают этот раздел два примера использования фоновых потоков выполнения. Первый демонстрирует, как реализовать выполнение длительных вычислений в фоновом потоке, чтобы они не влияли на отзывчивость пользовательского интерфейса, обслуживаемого основным потоком выполнения. Второй демонстрирует, как можно использовать фоновые потоки для работы с простейшими синхронными прикладными интерфейсами.

В примере 22.6 определяется функция smear(), которая принимает элемент <img> в виде аргумента. Она применяет эффект размытия для «смазывания» изображения вправо. Для реализации этого эффекта используется описанный в главе 21 прием копирования изображения в неотображаемый элемент <canvas> и последующего извлечения пикселей изображения с помощью объекта ImageData. Элементы <img> и <canvas> нельзя передать фоновому потоку выполнения с помощью метода postMessage(), но можно передать объект ImageData (подробности во врезке «Структурированные копии» выше). Пример 22.6 создает объект Worker и вызывает его метод postMessage(), чтобы передать ему изображение. Когда фоновый поток отправит обработанные пикселы изображения обратно, программный код скопирует их снова в элемент <canvas>, извлекая их как ресурс с URL-адресом вида data:// и устанавливая этот URL-адрес в качестве значения свойства src оригинального элемента <img>.

Пример 22.6. Создание фонового потока выполнения для обработки изображения

// Асинхронная замена изображения его смазанной версией.
// Используется так: <img src="testimage.jpg"/>
function smear(img) {
  // Создать неотображаемый элемент <canvas> того же размера, что и изображение
  var canvas = document.createElement("canvas");
  canvas.width = img.width;
  canvas.height = img.height;
  // Скопировать изображение в холст и извлечь его пикселы
  var context = canvas.getContext("2d");
  context.drawImage(img, 0, 0);
  var pixels = context.getImageData(0,0,img.width,img.height)
  // Отправить пикселы фоновому потоку выполнения
  var worker = new Worker("SmearWorker.js"); // Создать фоновый поток
  worker.postMessage(pixels); // Скопировать и отдать пикселы
  // Зарегистрировать обработчик для получения ответа от фонового потока
  worker.onmessage = function(e) {
    var smeared_pixels = e.data;                // Пикселы, полученные от потока
    context.putImageData(smeared_pixels, 0, 0); // Скопировать в холст
    img.src = canvas.toDataURL();               // А затем в изображение
    worker.terminate();                         // Остановить поток
    canvas.width = canvas.height = 0;           // Освободить память
  }
}

В примере 22.7 приводится программный код реализации фонового потока, используемого в примере 22.6. Основу этого примера составляет функция обработки изображения: модифицированная версия примера 21.10. Обратите внимание, что этот пример настраивает свою инфраструктуру обмена сообщениями единственной строчкой программного кода: обработчик события onmessage просто накладывает эффект смазывания на изображение и сразу же отправляет его обратно.

Пример 22.7. Обработка изображения в фоновом потоке выполнения

// Получает объект ImageData от основного потока выполнения, обрабатывает его
// и отправляет обратно
onmessage = function(e) { postMessage(smear(e.data)); }
// Смазывает пикселы в ImageData вправо, воспроизводя эффект быстрого движения.
// При обработке больших изображений этой функции приходится выполнять огромный объем
// вычислений, что может вызвать эффект подвисания пользовательского интерфейса,
// если использовать ее в основном потоке выполнения.
function smear(pixels) {
  var data = pixels.data,
    width = pixels.width,
    height = pixels.height;
  var n = 10, m = n-1; // Нем больше n, тем сильнее эффект смазывания
  for(var row = 0; row < height; row++) { // Для каждой строки
    var і = row*width*4 +4; // Индекс 2-го пиксела
    for(var col = 1; col < width; col++, і += 4) { // Для каждого столбца
      data[i] = (data[i] + data[i-4]*m)/n;     // Красная составляющая
      data[i+1] = (data[i+1] + data[i-3]*m)/n; // Зеленая
      data[i+2] = (data[i+2] + data[i-2]*m)/n; // Синяя
      data[i+3] = (data[i+3] + data[i-1]*m)/n; // Альфа-составляющая
    }
  }
  return pixels;
}

 Обратите внимание, что программный код в примере 22.7 может обрабатывать любое количество изображений, которые будут отправлены ему. Однако для простоты пример 22.6 создает новый объект Worker для обработки каждого изображения. Чтобы не плодить фоновые потоки, которые ничего не делают в ожидании новых сообщений, по завершении обработки изображения работа фонового потока завершается вызовом метода terminate().

Следующий пример демонстрирует, как с помощью фоновых потоков выполнения можно писать синхронный программный код и безопасно использовать его в клиентских сценариях на языке JavaScript. В разделе 18.1.2.1 было показано, как с помощью объекта XMLHttpRequest выполнять синхронные НТТР-запросы, и говорилось, что такой способ его использования в основном потоке выполнения является не лучшим решением. Однако в фоновом потоке вполне оправданно выполнять синхронные запросы, и в примере 22.8 демонстрируется реализация фонового потока выполнения, которая выполняет именно такие запросы. Его обработчик события onmessage принимает массив URL-адресов, использует синхронный прикладной интерфейс объекта XMLHttpRequest для извлечения их содержимого и затем посылает полученное текстовое содержимое в виде массива строк обратно основному потоку выполнения. Или, если какой-либо HTTP-запрос потерпит неудачу, возбуждает исключение, которое распространится до обработчика onerror объекта Worker.

Отладка фоновых потоков выполнения

Одним из прикладных интерфейсов, недоступных в объекте WorkerGlobalScope (по крайней мере, на момент написания этих строк), является прикладной интерфейс доступа к консоли и одна из самых ценных его функций - console.log(). Фоновые потоки не могут выводить текст в консоль и вообще не могут взаимодействовать с документом, поэтому их отладка может оказаться весьма трудным делом. Если фоновый поток возбудит исключение, основной поток получит событие «error» в объекте Worker. Но чаще бывает необходимо иметь в фоновом потоке хоть какой-нибудь способ выводить отладочные сообщения, которые будут видимы в веб-консоли броузера. Один из самых простых способов добиться этого - изменить протокол передачи сообщений, используемый для взаимодействия с фоновым потоком, чтобы он мог посылать отладочные сообщения. Так, в примере 22.6 можно было бы вставить следующий программный код в начало обработчика событий onmessage:

if (typeof e.data === "string") {
  console.log("Worker: " + e.data):
  return:
}

Благодаря этому дополнительному программному коду фоновый поток получает возможность отображать отладочные сообщения, просто передавая строки методу postMessage().

*******************************************

Пример 22.8. Выполнение синхронных HTTP-запросов в фоновом потоке

// Этот файл будет загружен вызовом конструктора Worker(), поэтому он будет выполняться
// в независимом потоке выполнения и может безопасно использовать синхронный прикладной
// интерфейс объекта XMLHttpRequest. В качестве сообщения фоновому потоку должен
// передаваться массив URL-адресов. Поток синхронно извлечет содержимое
// из указанных адресов и вернет его в виде массива строк,
onmessage = function(e) {
  var urls = e.data; // Входные данные: URL-адреса извлекаемого содержимого
  var contents = []; // Выходные данные: содержимое указанных URL-адресов
  for(var і = 0: і < urls.length; i++) {
    var url = urls[i]; // Для каждого URL-адреса
    var xhr = new XMLHttpRequest(); // Создать HTTP-запрос
    xhr.open("GET'', url, false); // false обеспечит синхронное выполн.
    xhr.send(); // Блокируется до выполнения запроса
    if (xhr.status !== 200) // Возбудить исключение при неудаче
      throw Error(xhr.status + " " + xhr.statusText + ": + url);
    contents.push(xhr.responseText); // Иначе сохранить содержимое
  }
  // Отослать массив содержимого URL-адресов обратно основному потоку
  postMessage(contents);
}

Оглавление книги


Генерация: 1.148. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз