Хотите — верьте, хотите — нет, но иногда разработчики приложений пишут код, который не работает. Или работает, но ужасающе неэффективно и пожирает память. Еще хуже, что неэффективный код создает у пользователей крайне негативное впечатление, побуждая их удалять приложение и оставлять отрицательные отзывы.
Я намерен исследовать распространенные проблемы с производительностью и эффективностью, с которыми вы можете столкнуться при создании приложений Windows Store на JavaScript. В этой статье я рассмотрю рекомендации по обработке ошибок с использованием Windows Library for JavaScript (WinJS). В следующей статье мы обсудим способы выполнения работы без блокировки UI-потока, в том числе используя Web Workers или новый WinJS.Utilities.Scheduler API в WinJS 2.0, появившийся в Windows 8.1. Я также представлю новую модель предсказуемого жизненного цикла объектов (predictable-object lifecycle model) в WinJS 2.0, сосредоточившись, в частности, на том, когда и как удалять элементы управления.
В каждой из этих областей я уделяю основное внимание:
- неэффективности или ошибкам, возможным в приложениях Windows Store, создаваемых на JavaScript;
- диагностическим средствам для поиска этих ошибок и неэффективной работы;
- WinJS API, средствам и рекомендациям, позволяющим решать специфические проблемы.
Я предоставлю кое-какой преднамеренно ошибочный код, но не волнуйтесь, я укажу в коде, что будет или не будет работать.
Для этих демонстраций я использую Visual Studio 2013, Windows 8.1 и WinJS 2.0. Многие из применяемых мной диагностических средств содержатся в Visual Studio 2013. Если вы не скачали самые свежие версии этих средств, то можете получить их с сайта Windows Dev Center (bit.ly/K8nkk1). Новые диагностические средства выпускаются через обновления Visual Studio, так что не забывайте периодически проверять наличие обновлений.
Я исхожу из того, что вы хорошо знакомы с разработкой приложений Windows Store на JavaScript. Если вы относительно недавно имеете дело с этой платформой, предлагаю вам начать с базового примера «Hello World» (bit.ly/vVbVHC), а потом изучить более сложный пример Hilo для JavaScript (bit.ly/SgI0AA).
Подготовка проекта-примера
Первым делом я создаю новый проект в Visual Studio 2013, используя шаблон Navigation App, который является хорошей отправной точкой для базового многостраничного приложения. Кроме того, я добавляю элемент управления NavBar (bit.ly/14vfvih) к странице default.html в корне решения, заменяя код AppBar, предоставленный шаблоном. Поскольку я собираюсь продемонстрировать несколько концепций, диагностических средств и методов программирования, я буду добавлять в приложение новую страницу для каждой демонстрации. Так мне будет гораздо проще переключаться между всеми тестовыми сценариями.
Полная HTML-разметка для NavBar показана на рис. 1. Скопируйте и вставьте этот код в ваше решение, если вы следуете моему примеру.
Рис. 1. Элемент управления NavBar
<!-- Панель глобальной навигации для приложения -->
<div id="navBar" data-win-control="WinJS.UI.NavBar">
<div id="navContainer"
data-win-control="WinJS.UI.NavBarContainer">
<div id="homeNav"
data-win-control="WinJS.UI.NavBarCommand"
data-win-options="{
location: '/pages/home/home.html',
icon: 'home',
label: 'Home page'
}">
</div>
<div id="handlingErrors"
data-win-control="WinJS.UI.NavBarCommand"
data-win-options="{
location: '/pages/handlingErrors/handlingErrors.html',
icon: 'help',
label: 'Handling errors'
}">
</div>
<div id="chainedAsync"
data-win-control="WinJS.UI.NavBarCommand"
data-win-options="{
location: '/pages/chainedAsync/chainedAsync.html',
icon: 'link',
label: 'Chained asynchronous calls'
}">
</div>
</div>
</div>
Подробнее о создании панели навигации полистайте некоторые статьи в рубрике «Современные приложения» Рэчел Аппель (Rachel Appel), например по ссылке msdn.microsoft.com/magazine/dn342878.
Вы можете запустить этот проект только с панелью навигации, однако щелчок любой из кнопок на этой панели вызовет исключение в navigator.js. Далее в этой статье я рассмотрю, как обрабатывать ошибки, возникающие в navigator.js. А пока напомню, что приложение всегда запускается с начальной страницей (home page) и что вам нужно щелкнуть приложение правой кнопкой мыши, чтобы на экране появилась панель навигации.
Обработка ошибок
Очевидно, лучший способ избегать ошибок — выпускать приложения, которые не вызывают ошибок. В идеальном мире каждый разработчик писал бы совершенный код, который никогда не вызывает краха и никогда не генерирует исключения. Увы, такого мира не существует.
Мало того что пользователи предпочитают полностью свободные от ошибок приложения, они еще и исключительно хорошо находят новые и креативные способы сломать приложение — да так, что вам и не снилось. В итоге вы должны включать в свои приложения отказоустойчивую обработку ошибок.
Ошибки в приложениях Windows Store, созданных на JavaScript и HTML, практически ничем не отличаются от ошибок в обычных веб-страницах. Когда ошибка возникает в объекте Document Object Model (DOM), который обеспечивает обработку ошибок (например, в элементах <script>, <style> или <img>), для этого элемента генерируется событие onerror. В случае ошибок в JavaScript-стеке вызовов, ошибка распространяется вверх по цепочке вызовов, пока не будет перехвачена (скажем, в блоке try/catch) или пока не достигнет объекта окна, вызвав событие window.onerror.
WinJS поддерживает несколько уровней обработки ошибок в вашем коде в дополнение к тому, уже предоставляется для обычных веб-страниц в Microsoft Web Platform. На фундаментальном уровне любая ошибка, не захваченная блоком try/catch или обработчиком onError, примененным к объекту WinJS.Promise (например, в вызове метода then или done), генерирует событие WinJS.Application.onerror. Вскоре я расскажу об этом.
На практике вы можете прослушивать ошибки на других уровнях, помимо Application.onerror. В случае WinJS и шаблонов, предоставляемых Visual Studio, также можно обрабатывать ошибки на уровне страницы и на уровне навигации. Если ошибка возникает при переходе к какой-либо странице и ее загрузке, инициируется обработка ошибок на уровне навигации, затем на уровне страницы и, наконец, на уровне приложения. Вы можете отменить ошибку на уровне навигации, но любые обработчики событий, примененные к обработчику ошибок на уровне страницы, все равно будут срабатывать.
В этой статье мы обсудим каждый уровень обработки ошибок, начиная с самого важного: события Application.onerror.
Обработка ошибок на уровне приложения
WinJS предоставляет событие WinJS.Application.onerror (bit.ly/1cOctjC) — базовую линию обороны вашего приложения от ошибок. Оно собирает все ошибки, захваченные window.onerror. Кроме того, оно захватывает обещания (promises), от которых исходят ошибки, и любые ошибки, возникающие в процессе управления событиями модели приложения. Хотя вы можете применить обработчик к событию window.onerror в своем приложении, лучше просто использовать Application.onerror для единой очереди отслеживаемых событий.
Мало того что пользователи предпочитают полностью свободные от ошибок приложения, они еще и исключительно хорошо находят новые и креативные способы сломать приложение — да так, что вам и не снилось.
Как только обработчик Application.onerror захватывает ошибку, вы должны решить, что с ней делать. Есть несколько вариантов.
- Если возникла критическая блокирующая ошибка, оповещайте о ней пользователя через диалог с сообщением. Такая ошибка влияет на продолжаемые операции приложения и может потребовать ввода от пользователя.
- В случае информационных и неблокирующих ошибок (например, неудачи в синхронизации или получении онлайновых данных) оповещайте пользователя с помощью всплывающего элемента (flyout) или встроенного сообщения (inline message).
- Если ошибка не влияет на работу пользователя, автоматически «глотайте» ее.
- В большинстве случаев записывайте ошибку в журнал трассировки (tracelog) (особенно в тот, который подключен к механизму анализа), чтобы можно было дистанционного получить необходимые вам данные. За доступными SDK для целей анализа обращайтесь в каталог Windows-сервисов на services.windowsstore.com и в списке слева щелкните Analytics (под «By service type»).
В этом примере я буду выводить диалоги с сообщениями. Откройте default.js (/js/default.js) и добавьте код, показанный на рис. 2, в основную анонимную функцию под обработчиком для события app.oncheckpoint.
Рис. 2. Добавление диалога с сообщением
app.onerror = function (err) {
var message = err.detail.errorMessage ||
(err.detail.exception && err.detail.exception.message) ||
"Indeterminate error";
if (Windows.UI.Popups.MessageDialog) {
var messageDialog =
new Windows.UI.Popups.MessageDialog(
message,
"Something bad happened ...");
messageDialog.showAsync();
return true;
}
}
Здесь обработчик события ошибки выводит сообщение, оповещающее пользователя об ошибке и о том, что она собой представляет. Обработчик события возвращает true, чтобы удерживать диалог с сообщением открытым, пока пользователь не закроет его. (Возврат true также информирует процесс WWAHost.exe о том, что ошибка обработана и что он может продолжить свою работу.)
Теперь я создам некоторые ошибки для этого кода, чтобы он их обработал. Я создам собственную ошибку, сгенерирую ее, а затем захвачу в обработчике событий. Для этого первого примера я добавляю новую папку с именем handlingErrors в папку pages. В эту папку я добавляю новый Page Control, щелкнув правой кнопкой мыши проект в Solution Explorer и выбрав Add | New Item. После добавления handlingErrors Page Control в проект Visual Studio генерирует три файла в папке handlingErrors (/pages/handlingErrors): handlingErrors.html, handlingErrors.js и handlingErrors.css.
Открываем handlingErrors.html и вставляем в тег <section> элемента body следующую простую разметку:
<!-- При щелчке эта кнопка генерирует собственную ошибку -->
<button id="throwError">Throw an error!</button>
Собственные ошибки
Application.onerror имеет некоторые ожидания в отношении обрабатываемых им ошибок. Лучший способ создать собственную ошибку — использовать объект WinJS.ErrorFromName (bit.ly/1gDESJC). Этот объект предоставляет стандартный интерфейс для обработчиков ошибок.
Для создания собственной ошибки без применения объекта ErrorFromName вам придется реализовать метод toString, который возвращает сообщение об ошибке.
Иначе, когда будет сгенерирована ваша ошибка, в отладчике Visual Studio и диалоге с сообщением будет показано «[Object object]». Каждый из них вызывает метод toString для объекта, но, поскольку такой метод не определен в промежуточном объекте, вызов пойдет по цепочке наследования прототипов, пока не будет найдено определение toString. Как только будет достигнут элементарный тип Object, в котором есть метод toString, произойдет вызов именно этого метода (а он просто выведет информацию об этом объекте).
Затем я открываю handlingErrors.js и добавляю обработчик события к кнопке в методе ready объекта PageControl (рис. 3). Я предоставил полное определение PageControl в handlingErrors.js для контекста.
Рис. 3. Определение handlingErrors PageControl
// Введение в шаблон Page Control см. в документации по ссылке
// http://go.microsoft.com/fwlink/?LinkId=232511
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/handlingErrors/ handlingErrors.html", {
ready: function (element, options) {
// ОШИБКА: этот код генерирует собственную ошибку
throwError.addEventListener("click", function () {
var newError = new WinJS.ErrorFromName(
"Custom error", "I'm an error!");
throw newError;
});
},
unload: function () {
// Реагируем на переходы с этой страницы
},
updateLayout: function (element) {
// Реагируем на изменения в разметке
}
});
})();
Теперь нажимаем F5 для запуска примера, переходим на страницу handlingErrors и щелкаем кнопку «Throw an error!». (Если вы следуете за мной, то увидите диалоговое окно от Visual Studio, информирующее о сгенерированной ошибке. Щелкните Continue, чтобы продолжить выполнение примера.) Потом появляется диалог с сообщением об ошибке (рис. 4).
Рис. 4. Собственная ошибка, отображаемая в диалоге с сообщением
Обработка ошибок на уровне страницы
Объект PageControl в WinJS предоставляет другой уровень обработки ошибок в приложении. WinJS будет вызывать метод IPageControlMembers.error, когда при загрузке страницы возникает какая-то ошибка. Однако после загрузки страницы ошибки метода IPageControlMembers.error захватываются обработчиком событий Application.onerror, игнорируя метод error страницы.
WinJS предоставляет событие WinJS.Application.onerror (bit.ly/1cOctjC) — базовую линию обороны вашего приложения от ошибок.
Я добавлю метод error к PageControl, который представляет страницу handleErrors. Метод error пишет в JavaScript-консоль в Visual Studio, используя WinJS.log. Функциональность протоколирования нужно запускать первой, поэтому, прежде чем пытаться использовать этот метод, я должен вызвать WinJS.Utilities.startLog. Кроме того, обратите внимание, что я проверяю наличие WinJS.log перед тем, как вызываю его.
Полный код для handleErrors.js (/pages/handleErrors/handleErrors.js) приведен на рис. 5.
Рис. 5. Полный листинг handleErrors.js
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/handlingErrors/ handlingErrors.html", {
ready: function (element, options) {
// ОШИБКА: этот код генерирует собственную ошибку
throwError.addEventListener("click", function () {
var newError = {
message: "I'm an error!",
toString: function () {
return this.message;
}
};
throw newError;
})
},
error: function (err) {
WinJS.Utilities.startLog({ type: "pageError", tags: "Page" });
WinJS.log && WinJS.log(err.message, "Page", "pageError");
},
unload: function () {
// TODO: реагировать на переходы с этой страницы
},
updateLayout: function (element) {
// TODO: реагировать на изменения в разметке
}
});
})();
Теперь я попробую запустить пример и снова щелкнуть кнопку «Throw an error!». Это даст то же поведение, что и раньше: Visual Studio захватит ошибку, а затем сработает Application.onerror. JavaScript-консоль не показывает никаких сообщений об ошибке потому, что эта ошибка была вызвана после загрузки страницы. Таким образом, ошибка была захвачена только обработчиком событий Application.onerror.
Зачем использовать обработку ошибок PageControl? Это особенно полезно для захвата и диагностирования ошибок в WinJS-элементах управления, которые создаются декларативно в HTML. Например, я добавляю следующую HTML-разметку в тег <section> в файле handleErrors.html (/pages/handleErrors/handleErrors.html), ниже элемента button:
<!-- ОШИБКА: AppBarCommands должны быть элементами button
по умолчанию, если только в свойстве type не указано иное -->
<div data-win-control="WinJS.UI.AppBarCommand"></div>
Теперь нажимаем F5, чтобы запустить пример и перейти на страницу handleErrors. И вновь диалог с сообщением отображается, пока не будет закрыт. Однако в JavaScript-консоли появляется следующее сообщение (чтобы увидеть его, вам понадобится переключиться на рабочий стол):
pageError: Page: Invalid argument: For a button, toggle, or flyout command, the element must be null or a button element
Заметьте, что обработка ошибок на уровне приложения проявляется даже в том случае, когда я обработал ошибку в PageControl (который протоколирует ошибку). Тогда как же перехватить ошибку на странице, не позволив ей подниматься вверх по иерархии приложения?
WinJS.log
Вызов WinJS.Utilities.startLog, показанный на рис. 5, запускает вспомогательную функцию WinJS.log, которая записывает вывод в JavaScript-консоль по умолчанию. Хотя это здорово помогает на этапе разработки в отладочных целях, это же не позволяет вам захватывать данные об ошибке после того, пользователи установили ваше приложение.
Для приложений, готовых к публикации и развертыванию, следует подумать о собственной реализации WinJS.log, которая вызывает механизм анализа. Это позволит вам дистанционно собирать данные о работе вашего приложения и тем самым исправлять незамеченные ошибки в будущих версиях этого приложения. Главное — обеспечить, чтобы клиентам было известно о таком сборе данных и о том, какие именно данные собираются механизмом анализа; обо всем этом следует четко написать в заявлении о конфиденциальности, прилагаемом к вашему приложению.
Обратите внимание: когда вы перезаписываете WinJS.log таким способом, функция WinJS.log будет захватывать весь вывод, который иначе отправлялся бы в JavaScript-консоль, в том числе обновления состояния от планировщика (Scheduler) и др. Вот почему вам нужно передавать описательное имя и значение типа в вызове WinJS.Utilities.startLog — это позволит вам отфильтровывать любые ненужные ошибки.
Лучший способ захватывать ошибки уровня страницы — добавить обработку ошибок в код навигации. В следующем разделе я продемонстрирую это.
Обработка ошибок уровня навигации
Когда я выполнял предыдущий тест, где обработчик событий app.onerror обрабатывал ошибку уровня страницы, казалось, что приложение остается на начальной странице. Тем не менее, по какой-то причине появлялась кнопка Back. При щелчке этой кнопки я попадал на (отключенную) страницу handlingErrors.html.
Это вызвано тем, что навигационный код в navigator.js (/js/navigator.js), который предоставляется шаблоном проекта Navigation App, все равно пытается перейти на страницу, хотя на ней произошла ошибка. Более того, он обеспечивает переход назад на начальную страницу и добавляет сбойную страницу в историю навигации. Вот почему я вижу кнопку Back на начальной странице после попытки перейти на handlingErrors.html.
Чтобы отменить ошибку в navigator.js, я заменяю функцию PageControlNavigator._navigating кодом, показанным на рис. 6. Вы видите, что функция navigating содержит вызов WinJS.UI.Pages.render, который возвращает объект Promise. Метод render пытается создать новый PageControl по переданному ему URI и вставить его в хост-элемент. Поскольку конечный PageControl содержит ошибку, возвращенный объект Promise тоже включает ошибку. Чтобы захватить ошибку, возникшую при навигации, я добавляю обработчик ошибок в параметр onError метода then, предоставляемого этим объектом Promise. В конечном счете это обеспечивает перехват ошибки, не давая ей вызвать генерацию события Application.onerror.
Рис. 6. Функция PageControlNavigator._navigating в navigator.js
// Прочий код PageControlNavigator...
// Реагируем на навигацию добавление новых страниц в DOM
_navigating: function (args) {
var newElement = this._createPageElement();
this._element.appendChild(newElement);
this._lastNavigationPromise.cancel();
var that = this;
this._lastNavigationPromise =
WinJS.Promise.as().then(function ()
{
return WinJS.UI.Pages.render(args.detail.location,
newElement, args.detail.state);
}).then(function parentElement(control) {
var oldElement = that.pageElement;
// Очистка и удаление предыдущего элемента
if (oldElement.winControl) {
if (oldElement.winControl.unload) {
oldElement.winControl.unload();
}
oldElement.winControl.dispose();
}
oldElement.parentNode.removeChild(oldElement);
oldElement.innerText = "";
},
// Отображаем любые ошибки, вызванные элементом управления
// "страница", очищаем стек обратных вызовов (backstack)
// и отменяем ошибку
function (err) {
var messageDialog =
new Windows.UI.Popups.MessageDialog(err.message,
"Sorry, can't navigate to that page.");
messageDialog.showAsync()
nav.history.backStack.pop();
return true;
});
args.detail.setPromise(this._lastNavigationPromise);
},
// Прочий код PageControlNavigator...
Заметьте, что изменение navigator.js — вполне нормальная процедура. Хотя он предоставляется шаблоном проекта Visual Studio, это часть кода вашего приложения, и вы можете в любой момент его модифицировать.
В функцию _navigating я добавил обработчик ошибок для конечного вызова promise.then. Этот обработчик показывает диалог с сообщением, как и в случае обработки ошибок на уровне приложения, а затем отменяет ошибку, возвращая true. Кроме того, он удаляет проблемную страницу из истории навигации.
Вновь запустив пример и перейдя на страницу handlingErrors.html, я вижу диалог с сообщением, который информирует меня о неудачной попытке перехода. Соответствующий диалог от обработчика ошибок уровня приложения не появляется.
Объекты обещаний в WinJS
Создание объектов обещаний (promises) и соединение их в цепочку, а также рекомендации о том, как лучше это делать, — все это изложено во множестве других источников, поэтому я не стану обсуждать данную тему в своей статье. Если вам нужно больше информации, почитайте публикацию в блоге Крейга Брокшмидта (Kraig Brockschmidt) по ссылке bit.ly/1cgMAnu или загляните в «Appendix A» в его бесплатной электронной книге «Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition» (bit.ly/1dZwW1k).
Выявление источника ошибки в асинхронных цепочках
При разработке приложений на JavaScript мне часто требуется, чтобы за одной асинхронной задачей следовала другая, для чего я создаю цепочки объектов обещаний (promise). Объединенные в цепочку объекты обещаний будут продолжать выполнение задач, даже если один из объектов обещаний в цепочке вернет ошибку. Лучший стиль — всегда заканчивать цепочку объектов обещаний вызовом метода done. Функция done генерирует любые ошибки, которые могли быть захвачены обработчиком ошибок для любых предыдущий выражений then. То есть мне не нужно определять функции error для каждого объекта обещания в цепочке.
Всегда заканчивайте цепочку объектов обещаний вызовом метода done.
Даже в этом случае отслеживания источника ошибок может быть трудной задачей в очень длинных цепочках, когда эти ошибки захватываются вызовом promise.done. Соединенные в цепочку объекты promise могут включать множество задач, и любая из них может завершиться неудачей. Я мог бы поставить точку прерывания в каждую задачу, чтобы увидеть, где вылезает ошибка, но это потребовало бы слишком много времени.
И здесь Visual Studio 2013 приходит к нам на помощь. Окно Tasks (введенное в Visual Studio 2010) было обновлено для поддержки и отладки асинхронного JavaScript-кода. В окне Tasks показываются все активные и завершенные задачи в тот или иной момент времени в коде вашего приложения.
Для следующего примера я добавлю в решение новую страницу, чтобы продемонстрировать этот замечательный инструмент. В данном решении я создаю новую папку с именем chainedAsync в папке pages, а затем добавляю новый Page Control с именем chainAsync.html (который создает /pages/chainedAsync/chainedAsync.html и связанные файлы .js и .css).
В chainedAsync.html в теги <section> я вставляю следующую разметку:
<!-- ОШИБКА: щелчок этой кнопки запускает цепную реакцию с ошибкой -->
<p><button id="startChain">Start the error chain</button></p>
<p id="output"></p>
В chainedAsync.js я добавляю в метод ready страницы обработчик (рис. 7) для событий click кнопки startChain.
Рис. 7. Содержимое функции PageControl.ready в chainedAsync.js
startChain.addEventListener("click", function () {
goodPromise().
then(function () {
return goodPromise();
}).
then(function () {
return badPromise();
}).
then(function () {
return goodPromise();
}).
done(function () {
// Это не должно вызываться
},
function (err) {
document.getElementById('output').innerText = err.toString();
});
});
Наконец, я определяю в chainAsync.js функции goodPromise и badPromise (рис. 8), чтобы они были доступны в методах объекта PageControl.
Рис. 8. Определения функций goodPromise и badPromise в chainAsync.js
function goodPromise() {
return new WinJS.Promise(function (comp, err, prog) {
try {
comp();
} catch (ex) {
err(ex)
}
});
}
// ОШИБКА: это возвращает promise с ошибкой
function badPromise() {
return WinJS.Promise.wrapError("I broke my promise :(");
}
Я снова запускаю пример, перехожу на страницу «Chained asynchronous», а затем щелкаю кнопку Start the error chain. После небольшого ожидания под кнопкой появится сообщение «I broke my promise :(».
Теперь нужно отследить, где возникла ошибка, и понять, как ее исправить. (Очевидно, что в подобной придуманной ситуации я точно знаю, где произошла ошибка. В целях обучения я забуду, что это badPromise ввел ошибку в цепочку моих объектов promise.)
Чтобы узнать, где именно произошла ошибка в цепочке promise, я помещу точку прерывания в обработчик ошибок, определенный в вызове done в обработчике события click для кнопки startChain, как показано на рис. 9.
Рис. 9. Позиция точки прерывания в chainedAsync.html
Снова запускаем тот же тест, и, когда я возвращаюсь в Visual Studio, выполнение программы останавливается на точке прерывания. Потом я открываю окно Tasks (Debug | Windows | Tasks), чтобы понять, какие задачи активны в данный момент. Результаты приведены на рис. 10.
Рис. 10. Окно Tasks в Visual Studio 2013, показывающее ошибку
На первый взгляд ничто в этом окне не указывает на то, что именно вызвало ошибку. В окне перечислены пять задач, каждая из которых помечена как активная. Однако при более внимательном рассмотрении видно, что одна из активных задач — это Scheduler, ставящий в очередь ошибки обещания, и это выглядит обещающе (пожалуйста, простите меня за этот банальный каламбур).
(Если вас интересует, что такое Scheduler, советую прочитать следующую статью в этой серии, где я буду обсуждать новый Scheduler API в WinJS.)
Задержив курсор мыши над соответствующей строкой (ID 120 на рис. 10) в окне Tasks, я получаю целевое представление стека вызовов для этой задачи. Я вижу несколько обработчиков ошибок, и — о, чудо! — badPromise находится рядом с началом этого стека вызовов. Когда я дважды щелкаю эту строку, Visual Studio перенаправляет меня прямо на строку кода в badPromise, который вызвал ошибку. На практике мне пришлось бы теперь диагностировать, почему badPromise вызывает ошибку.
Заключение
WinJS предоставляет несколько уровней обработки ошибок в приложении — это гораздо больше, чем блок try-catch-finally (пусть и очень надежный). Хорошо проработанное приложение должно использовать соответствующий уровень обработки ошибок, чтобы у пользователя сложилось максимально благоприятное впечатление. В этой статье я продемонстрировал, как включать в приложение обработку ошибок уровней приложения, страницы и навигации. Я также показал, как использовать некоторые новые инструменты в Visual Studio 2013 для выявления причин ошибок в цепочке объектов обещаний (chained promises).
В следующей статье из этой серии я исследую некоторые методы, позволяющие сделать так, чтобы приложения Windows Store работали еще лучше. Я рассмотрю Web Worker, новый Scheduler API и новый шаблон dispose в WinJS 2.0.