Несанкционированное воспроизведение запрещено
1. Обработка дорогостоящих вычислений
Возможно, самым сложным аспектом разработки сложных приложений Javascript является однопоточный характер пользовательского интерфейса. Лучшая ситуация, когда Javascript обрабатывает взаимодействие с пользователем, — это медленный ответ, а худшая — отсутствие ответа, вызывающее зависание браузера (при выполнении Javascript все операции обновления на странице приостанавливаются). В связи с этим крайне важно свести все сложные операции (любые вычисления длительностью более 100 мс) до управляемого уровня. Кроме того, если сценарий не останавливается после работы в течение как минимум 5 секунд, некоторые браузеры (например, Firefox и Opera) генерируют окно с подсказкой, предупреждающее пользователя о том, что сценарий не отвечает.
Это явно нежелательно, и создавать неотзывчивый интерфейс — это нехорошо. Однако это почти наверняка тот случай, когда вам необходимо обрабатывать большие объемы данных (например, обрабатывать тысячи элементов DOM).
В это время таймер особенно полезен. Поскольку таймер может эффективно приостановить выполнение кода Javascript, он также может предотвратить зависание исполняемого кода браузером (до тех пор, пока отдельного кода недостаточно для зависания браузера). Имея это в виду, мы можем включить обычные, интенсивные, циклические вычисления в неблокирующие вычисления. Давайте рассмотрим следующий пример, где требуется этот тип вычислений.
Долгосрочная задача:
<table><tbody></tbody></table>
// Нормальный, интенсивный, режим работы
var table = document.getElementsByTagName("tbody");
для (вар я = 0; я <2000; я++) {
var tr = document.createElement("tr");
для (вар т = 0; т < 6; т++){
var td = document.createElement("td");
td.appendChild(document.createTextNode("" + t));
tr.appendChild(тд);
}
table.appendChild(tr);
}
}
В этом примере мы создаем в общей сложности 26 000 узлов DOM и заполняем числами таблицу. Это слишком дорого и, скорее всего, приведет к зависанию браузера и предотвращению нормального взаимодействия с пользователем. Мы можем ввести сюда таймеры и получить другие, возможно, лучшие результаты.
Используйте таймеры для разделения долго выполняющихся задач:
<table><tbody></tbody></table>
var table = document.getElementsByTagName("tbody");
вар я = 0, макс = 1999;
setTimeout(функция(){
for ( var шаг = я + 500; я < шаг; я++ ) {
var tr = document.createElement("tr");
для (вар т = 0; т < 6; т++){
var td = document.createElement("td");
td.appendChild(document.createTextNode("" + t));
tr.appendChild(тд);
}
}
table.appendChild(tr);
}
если (я <макс)
setTimeout(аргументы.callee, 0);
}, 0);
В нашем модифицированном примере мы разделили интенсивные вычисления на четыре части, каждая из которых создает 6500 узлов. Эти вычисления вряд ли нарушат нормальную работу браузера. В худшем случае эти числа могут быть скорректированы в любой момент (например, изменить их в пределах 250–500, чтобы каждая из наших ячеек генерировала 3500 узлов DOM). Но что самое впечатляющее, так это то, как мы изменили наш код, чтобы адаптироваться к новой асинхронной системе. Нам нужно проделать дополнительную работу, чтобы гарантировать, что числа для элементов генерируются правильно (цикл не заканчивается навсегда). Код очень похож на наш оригинальный. Обратите внимание, что мы используем замыкания для поддержания состояния итерации между фрагментами кода. Без замыканий этот код, несомненно, будет более сложным.
Использование этой технологии по сравнению с оригиналом имеет явные изменения. Длительные зависания браузера сменяются 4-мя визуальными обновлениями страниц. Хотя браузер пытается выполнить эти фрагменты кода как можно быстрее, он также отображает изменения DOM (например, массовые обновления) после каждого шага таймера. В большинстве случаев пользователи не знают об обновлениях такого типа, но важно помнить, что они случаются.
Есть одна ситуация, когда эта технология подойдет именно мне — приложение, которое я создал для расчета расписаний студентов. Поначалу приложение было типичным CGI (клиент общался с сервером, сервер рассчитывал расписание и возвращал его). Но я изменил его и вынес все расчеты расписания на клиент. Вот такой вид расчетов расписания:
Эти вычисления довольно дороги (необходимо пройти тысячи перестановок, чтобы найти правильный ответ). Данная проблема решается путем разделения расчета расписания на фактические единицы (обновление пользовательского интерфейса завершенной частью). В конце концов пользователи предоставили пользовательский интерфейс, который оказался быстрым, отзывчивым и удобным в использовании.
Часто удивляешься, насколько полезными могут быть технологии. Вы обнаружите, что он часто используется в долго выполняющихся программах, например в тестовых блоках (мы обсудим это в конце этой главы). Что еще более важно, эта технология показывает нам, как легко обойти ограничения среды браузера, одновременно предоставляя пользователям богатый опыт.