Выразительный JavaScript (Хавербеке) - страница 189

Обработка ошибок в асинхронном коде ещё сложнее, чем в синхронном. Поскольку нам часто приходится отделять часть работы и размещать её в функции обратного вызова, область видимости блока >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, а для старых версий его можно использовать в виде библиотеки.