Обработка ошибок в асинхронном коде ещё сложнее, чем в синхронном. Поскольку нам часто приходится отделять часть работы и размещать её в функции обратного вызова, область видимости блока >try
теряет смысл. В следующем коде исключение не будет поймано, потому что вызов >backgroundReadFile
возвращается сразу же. Затем управление уходит из блока >try
, и функция из него не будет вызвана.
>try {
> backgroundReadFile("example/data.txt", function(text) {
> if (text != "expected")
> throw new Error("That was unexpected");
> });
>} catch (e) {
> console.log("Hello from the catch block");
>}
Чтобы обрабатывать неудачные запросы, придётся передавать дополнительную функцию в нашу обёртку, и вызывать её в случае проблем. Другой вариант – использовать соглашение, что если запрос не удался, то в функцию обратного вызова передаётся дополнительный аргумент с описанием проблемы. Пример:
>function getURL(url, callback) {
> var req = new XMLHttpRequest();
> req.open("GET", url, true);
> req.addEventListener("load", function() {
> if (req.status < 400)
> callback(req.responseText);
> else
> callback(null, new Error("Request failed: "
> req.statusText));
> });
> req.addEventListener("error", function() {
> callback(null, new Error("Network error"));
> });
> req.send(null);
>}
Мы добавили обработчик события >error
, который сработает при проблеме с вызовом. Также мы вызываем функцию обратного вызова с аргументом >error
, когда запрос завершается со статусом, говорящим об ошибке.
Код, использующий >getURL
, должен проверять не возвращена ли ошибка, и обрабатывать её, если она есть.
>getURL("data/nonsense.txt", function(content, error) {
> if (error != null)
> console.log("Failed to fetch nonsense.txt: " + error);
> else
> console.log("nonsense.txt: " + content);
>});
С исключениями это не помогает. Когда мы совершаем последовательно несколько асинхронных действий, исключение в любой точке цепочки в любом случае (если только вы не обернули каждый обработчик в свой блок >try/catch
) вывалится на верхнем уровне и прервёт всю цепочку.
Тяжело писать асинхронный код для сложных проектов в виде простых обратных вызовов. Очень легко забыть проверку на ошибку или позволить неожиданному исключению резко прервать программу. Кроме того, организация правильной обработки ошибок и проход ошибки через несколько последовательных обратных вызовов очень утомительна.
Предпринималось множество попыток решить эту проблему дополнительными абстракциями. Одна из наиболее удачных попыток называется обещаниями (promises). Обещания оборачивают асинхронное действие в объект, который может передаваться и которому нужно сделать какие-то вещи, когда действие завершается или не удаётся. Такой интерфейс уже стал частью текущей версии JavaScript, а для старых версий его можно использовать в виде библиотеки.