Fetch async await обработка ошибок

Fetch API — это инструмент для выполнения сетевых запросов в веб-приложениях. При базовом использовании fetch() — довольно простой метод, но у него есть много нюансов. Например, мы не можем прервать fetch-запрос.

В этой статье рассмотрим сценарии использования fetch() вместе с другой возможностью языка — синтаксисом async/await. Разберемся, как получать данные, обрабатывать ошибки и отменять запросы.

Fetch API позволяет делать HTTP-запросы (GET, POST и т. д.) и обмениваться данными с сервером. Это более удобный аналог XHR.

Чтобы выполнить запрос, просто вызовите функцию fetch():

const response = await fetch(resource[, options]);
  • Первый параметр resource — это URL-адрес запроса и объект Request.
  • Второй (необязательный) параметр options — это конфигурация запроса. Можно настроить method, header, body, credentials и другие опции.

Функция fetch выполняет запрос и возвращает промис, который будет ждать, когда запрос завершится. После этого промис выполняется (resolve) с объектом Response (ответ сервера). Если во время запроса произошла какая-то ошибка, промис переходит в состояние rejected.

Синтаксис async/await прекрасно сочетается с fetch() и помогает упростить работу с промисами. Давайте для примера сделаем запрос списка фильмов:

async function fetchMovies() {
  const response = await fetch('/movies');
  // ждем выполнения запроса
  console.log(response);
}

Функция fetchMovies асинхронная, используем для ее создания ключевое слово async. Внутри она использует await, чтобы дождаться выполнения асинхронной операции fetch.

Внутри функции выполняется запрос на урл /movies. Когда он успешно завершается, мы получаем объект response с ответом сервера. Дальше в статье мы разберемся, как извлечь данные из этого объекта.

Получение JSON

Из объекта response, который возвращается из await fetch() можно извлечь данные в нескольких разных форматах. Чаще всего используется JSON:

async function fetchMoviesJSON() {
  const response = await fetch('/movies');
  const movies = await response.json();
  return movies;
}
fetchMoviesJSON().then(movies => {
  movies; // полученный список фильмов
});

Итак, чтобы извлечь полученные данные в виде JSON, нужно использовать метод response.json(). Этот метод возвращает промис, так что придется снова воспользоваться синтаксисом await, чтобы дождаться его выполнения: await response.json().

Кроме того, у объекта Response есть еще несколько полезных методов (все методы возвращают промисы):

  • response.json() возвращает промис, который резолвится в JSON-объект;
  • response.text() возвращает промис, который резолвится в обычный текст;
  • response.formData() возвращает промис, который резолвится в объект FormData;
  • response.blob() возвращает промис, который резолвится в Blob (файлоподобный объект с необработанными данными);
  • response.arrayBuffer()() возвращает промис, который резолвится в ArrayBuffer (необработанные двоичные данные).

Обработка ошибок

Для разработчиков, которые только начинают работать с fetch, может быть непривычным то, что этот метод не выбрасывает исключение, если сервер возвращает «плохой» HTTP-статус (клиентские 400-499 или серверные 500-599 ошибки).

Попробуем для примера обратиться к несуществующей странице /oops. Этот запрос, как и ожидается, завершается со статусом 404.

async function fetchMovies404() {
  const response = await fetch('/oops');
  
  response.ok;     // => false
  response.status; // => 404
  const text = await response.text();
  return text;
}
fetchMovies404().then(text => {
  text; // => 'Page not found'
});

Из объекта response мы можем узнать, что запрос не удался, однако метод fetch() не выбрасывает ошибку, а считает этот запрос завершенным.

Промис, возвращаемый функцией fetch, отклоняется только в том случае, если запрос не может быть выполнен или ответ не может быть получен, например, из-за проблем с сетью (нет подключения, хост не найден, сервер не отвечает).

Но к счастью, у нас есть поле response.ok, с помощью которого мы можем отловить плохие статусы. Оно принимает значение true только если статус ответа 200-299.

Если вы хотите получать ошибку для всех неудачных запросов, просто выбрасывайте ее вручную:

async function fetchMoviesBadStatus() {
  const response = await fetch('/oops');
  if (!response.ok) {
    const message = `An error has occured: ${response.status}`;
    throw new Error(message);
  }
  const movies = await response.json();
  return movies;
}
fetchMoviesBadStatus().catch(error => {
  error.message; // 'An error has occurred: 404'
});

Отмена fetch-запроса

К сожалению, Fetch API не предоставляет нам никакой возможности отменить запущенный запрос. Но это можно сделать с помощью AbortController.

Чтобы объединить эти инструменты, нужно сделать 3 действия:

// Шаг 1. Создать экземпляр AbortController до начала запроса
const controller = new AbortController();

// Step 2: Передать в параметры запроса fetch() controller.signal
fetch(..., { signal: controller.signal });

// Step 3: Отменить запрос методом controller.abort при необходимости
controller.abort();

Давайте для примера создадим маленькое приложение с двумя кнопками, одна из которых будет запускать fetch-запрос, а вторая прерывать его.

let controller = null;

// Обрабатываем клики по первой кнопке
fetchMoviesButton.addEventListener('click', async () => {
  controller = new AbortController();
  try {
    const response = await fetch('/movies', { 
      signal: controller.signal 
    });
  } catch (error) {
    console.log('Fetch error: ', error);
  }
  controller = null;
});

// Обрабатываем клики по второй кнопке
cancelFetchButton.addEventListener('click', () => {
  if (controller) {
    controller.abort();
  }
});

Демо:

Кликните по кнопке Fetch movies, чтобы запустить запрос, а затем по кнопке Cancel fetch, чтобы отменить его. При этом возникнет ошибка, которую поймает блок .catch().

Экземпляр AbortController одноразовый, его нельзя переиспользовать для нескольких запросов. Поэтому для каждого вызова fetch нужно создавать новый инстанс.

  • Как прервать fetch-запрос, если он не завершился через определенное время

Параллельные fetch запросы

Чтобы выполнять fetch-запросы параллельно, можно воспользоваться методом Promise.all().

Например, запустим сразу два запроса — для получения фильмов и для получения категорий фильмов:

async function fetchMoviesAndCategories() {
  const [moviesResponse, categoriesResponse] = await Promise.all([
    fetch('/movies'),
    fetch('/categories')
  ]);
  const movies = await moviesResponse.json();
  const categories = await categoriesResponse.json();
  return [movies, categories];
}
fetchMoviesAndCategories().then(([movies, categories]) => {
  movies;     // список фильмов
  categories; // список категорий
}).catch(error => {
  // один из запросов завершился с ошибкой
});

Фрагмент кода await Promise.all([]) запускает запросы параллельно и ожидает, когда все они перейдут в состояние resolved.

Если один из запросов завершится с ошибкой, то Promise.all тоже выбросит ошибку.

Если же вы хотите, чтобы выполнились все запросы, даже если несколько из них упали, используйте метод Promise.allSettled().

Заключение

Вызов функции fetch() запускает запрос к серверу и возвращает промис. Когда запрос успешно завершается, промис переходит в состояние resolved и возвращает объект ответа (response), из которого можно извлечь данные в одном из доступных форматов (JSON, необработанный текст или Blob).

Так как fetch возвращает промис, мы можем использовать синтаксис async/await, чтобы упростить код: response = await fetch().

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

I have stripe async code in my React app, and trying to add error handling in my code but have no idea how to handle it. i know how to do it with .then() but async/await is new to me

EDITED

added .catch() i got errors in network tab in response tab.
but i can log it to console?

    submit = async () => {
    const { email, price, name, phone, city, street, country } = this.state;
    let { token } = await this.props.stripe
      .createToken({
        name,
        address_city: city,
        address_line1: street,
        address_country: country
      })
      .catch(err => {
        console.log(err.response.data);
      });

    const data = {
      token: token.id,
      email,
      price,
      name,
      phone,
      city,
      street,
      country
    };

    let response = await fetch("/charge/pay", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    }).catch(err => {
      console.log(err.response.data);
    });
    console.log(response);
    if (response.ok)
      this.setState({
        complete: true
      });
  };

thanks

Время на прочтение
9 мин

Количество просмотров 21K

Доброго времени суток, друзья!

Сравнительно новыми дополнениями JavaScript являются асинхронные функции и ключевое слово await. Эти возможности в основном являются синтаксическим сахаром над обещаниями (промисами), облегчая написание и чтение асинхронного кода. Они делают асинхронный код похожим на синхронный. Данная статья поможет вам разобраться, что к чему.

Условия: базовая компьютерная грамотность, знание основ JS, понимание азов асинхронного кода и обещаний.
Цель: понять, как устроены обещания, и как они используются.

Основы async/await

Использование async/await состоит из двух частей.

Ключевое слово async

Прежде всего, у нас есть ключевое слово async, которое мы помещаем перед объявлением функции, чтобы сделать ее асинхронной. Асинхронная функция — это функция, которая предвосхищает возможность использования ключевого слова await для запуска асинхронного кода.

Попробуйте набрать в консоли браузера следующее:

function hello(){ return 'Hello' }
hello()

Функция вернет ‘Hello’. Ничего необычно, верно?

Но что если мы превратим ее в асинхронную функцию? Попробуйте сделать следующее:

async function hello(){ return 'Hello' }
hello()

Теперь вызов функции возвращает обещание. Это одна из особенностей асинхронных функций — они возвращают значения, которые гарантировано преобразуются в обещания.

Вы также можете создать асинхронное функциональное выражения, например, так:

let hello = async function(){ return hello() }
hello()

Также можно использовать стрелочные функции:

let hello = async () => { return 'Hello' }

Все эти функции делают одно и тоже.

Для того, чтобы получить значение завершенного обещания, мы можем использовать блок .then():

hello().then((value) => console.log(value))

… или даже так:

hello().then(console.log)

Таким образом, добавление ключевого слова async заставляет функцию возвращать обещание вместо значения. Кроме того, это позволяет синхронным функциям избегать любых накладных расходов, связанных с запуском и поддержкой использования await. Простое добавление async перед функцией обеспечивает автоматическую оптимизацию кода движком JS. Круто!

Ключевое слово await

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

Вы можете использовать await при вызове любой функции, возвращающей обещание, включая функции Web API.

Вот тривиальный пример:

async function hello(){
    return greeting = await Promise.resolve('Hello')
}

hello().then(alert)

Разумеется, приведенный код бесполезен, он лишь служит демонстрацией синтаксиса. Давайте двигаться дальше и посмотрим на реальный пример.

Переписываем код на обещаниях с использованием async/await

Возьмем пример с fetch из предыдущей статьи:

fetch('coffee.jpg')
.then(response => response.blob())
.then(myBlob => {
    let objectURL = URL.createObjectURL(myBlob)
    let image = document.createElement('img')
    image.src = objectURL
    document.body.appendChild(image)
})
.catch(e => {
    console.log('There has been a problem with your fetch operation: ' + e.message)
})

У вас уже должны быть понимание того, что такое обещания и как они работают, но давайте перепишем этот код с использованием async/await, чтобы увидеть насколько все стало проще:

async function myFetch(){
    let response = await fetch('coffee.jpg')
    let myBlob = await response.blob()

    let objectURL = URL.createObjectURL(myBlob)
    let image = document.createElement('img')
    image.src = objectURL
    document.body.appendChild(image)
}

myFetch().catch(e => {
    console.log('There has been a problem with your fetch operation: ' + e.message)
})

Это делает код намного проще и более легким для понимания — никаких блоков .then()!

Использование ключевого слова async превращает функцию в обещание, поэтому мы можем использовать смешанный подход из обещаний и await, выделив вторую часть функции в отдельный блок с целью повышения гибкости:

async function myFetch(){
    let response = await fetch('coffee.jpg')
    return await response.blob()
}

myFetch().then((blob) => {
    let objectURL = URL.createObjectURL(blob)
    let image = document.createElement('image')
    image.src = objectURL
    document.body.appendChild(image)
}).catch(e => console.log(e))

Вы можете переписать пример или запустить наше живое демо (см. также исходный код).

Но как это работает?

Мы обернули код внутри функции и добавили ключевое слово async перед ключевым словом function. Вам нужно создать асинхронную функцию, чтобы определить блок кода, в котором будет запускаться асинхронный код; await работает только внутри асинхронных функций.

Еще раз: await работает только в асинхронных функциях.

Внутри функции myFetch() код очень сильно напоминает версию на обещаниях, но с некоторыми отличиями. Вместо использования блока .then() после каждого метода, основанного на обещаниях, достаточно добавить ключевое слово await перед вызовом метода и присвоить значение переменной. Ключевое слово await заставляет движок JS приостановить выполнение кода на данной строке, позволяя выполняться другому коду, пока асинхронная функция не вернет результат. Как только она выполнится, код продолжит выполнение со следующей строки.
Например:

let response = await fetch('coffee.jpg')

Значение, возвращаемое обещанием fetch(), присваивается переменной response, когда данное значение становится доступным, и «парсер» останавливается на этой линии до завершения обещания. Как только значение становится доступным, парсер переходит к следующей строчке кода, которая создает Blob. Эта строчка также вызывает основанный на обещаниях асинхронный метод, поэтому здесь мы также используем await. Когда результат операции возвращается, мы возвращаем его из функции myFetch().

Это означает, что когда мы вызываем функцию myFetch(), она возвращает обещание, поэтому мы можем добавить к ней .then(), внутри которого мы обрабатываем отображение изображения на экране.

Вы, наверное, думаете «Вот здорово!», и вы правы — меньше блоков .then() для оборачивания кода, все это выглядит как синхронный код, поэтому он интуитивно понятен.

Добавляем обработку ошибок

Если вы хотите добавить обработку ошибок, у вас есть несколько вариантов.

Вы можете использовать синхронную структуру try…catch вместе с async/await. Этот пример является расширенной версией приведенного выше кода:

async function myFetch(){
    try{
        let response = await fetch('coffee.jpg')
        let myBlob = await response.blob()

        let objectURL = URL.createObjectURL(myBlob)
        let image = document.createElement('img')
        image.src = objectURL
        document.body.appendChild(image)
    } catch(e){
        console.log(e)
    }
}

myFetch()

Блок catch(){} принимает объект ошибки, который мы назвали «e»; теперь мы можем вывести его в консоль, это позволит нам получить сообщение о том, в каком месте кода произошла ошибка.

Если вы хотите использовать вторую версию кода, показанного выше, вам следует просто продолжать использовать гибридный подход и добавить блок .catch() в конец вызова .then(), следующим образом:

async function myFetch(){
    let response = await fecth('coffee.jpg')
    return await response.blob()
}

myFetch().then((blob) => {
    let objectURL = URL.createObjectURL
    let image = document.createElement('img')
    image.src = objectURL
    document.body.appendChild(image)
}).catch(e => console.log(e))

Это возможно, поскольку блок .catch() будет перехватывать ошибки, возникающие как в асинхронной функции, так и в цепочке обещаний. Если вы используете здесь блок try/catch, то не сможете обрабатывать ошибки, возникающие при вызове функции myFetch().

Вы можете найти оба примера на GitHub:
simple-fetch-async-await-try-catch.html (см. исходный код)

simple-fetch-async-await-promise-catch.html (см. исходный код)

Ожидание Promise.all()

Async/await основан на обещаниях, так что вы можете использовать все возможности последних. К ним, в частности, относится Promise.all() — вы легко можете добавить await к Promise.all(), чтобы записать все возвращаемые значения способом, похожим на синхронный код. Снова возьмем пример из предыдущей статьи. Держите вкладку с ним открытой, чтобы сравнить с показанным ниже кодом.

С async/await (см. живое демо и исходный код) он выглядит так:

async function fetchAndDecode(url, type){
    let repsonse = await fetch(url)

    let content

    if(type === 'blob'){
        content = await response.blob()
    } else if(type === 'text'){
        content = await response.text()
    }

    return content
}

async function displayContent(){
    let coffee = fetchAndDecode('coffee.jpg', 'blob')
    let tea = fetchAndDecode('tea.jpg', 'blob')
    let description = fetchAndDecode('description.txt', 'text')

    let values = await Promise.all([coffee, tea, description])

    let objectURL1 = URL.createObjectURL(values[0])
    let objectURL2 = URL.createObjectURL(values[1])
    let descText = values[2]

    let image1 = document.createElement('img')
    let image2 = document.createElement('img')
    image1.src = objectURL1
    image2.src = objectURL2
    document.body.appendChild(image1)
    document.body.appendChild(image2)

    let para = document.createElement('p')
    para.textContent = descText
    document.body.appendChild(para)
}

displayContent()
.catch(e => console.log(e))

Мы легко превратили функцию fetchAndDecode() в асинхронную с помощью парочки изменений. Обратите внимания на строчку:

let values = await Promise.all([coffee, tea, description])

Используя await, мы получаем результаты трех обещаний в переменной values, способом, похожим на синхронный код. Мы должны обернуть всю функцию в новую асинхронную функцию, displayContent(). Мы не достигли сильного сокращения кода, но получили возможность извлечь большую часть кода из блока .then(), что обеспечивает полезное упрощение и делает код более читаемым.

Для обработки ошибок мы добавили блок .catch() в наш вызов displayContent(); он обрабатывает ошибки обеих функций.

Запомните: также можно использовать блок .finally() для получения отчета о выполнении операции — вы можете увидеть это в действии в нашем живом демо (см. также исходный код).

Недостатки async/await

Async/await имеет парочку недостатков.

Async/await делает код похожим на синхронный и в некотором смысле заставляет его вести себя более синхронно. Ключевое слово await блокирует выполнение следующего за ним кода до завершения обещания, как это происходит в синхронной операции. Это позволяет выполняться другим задачам, но ваш собственный код является заблокированным.

Это означает, что ваш код может быть замедлен большим количеством ожидающих обещаний, следующих друг за другом. Каждый await будет ждать завершения предыдущего, в то время как мы хотели бы, чтобы обещания начали выполняться одновременно, так будто мы не используем async/await.

Существует шаблон проектирования, позволяющий смягчить эту проблему — отключение всех процессов обещаний путем сохранения объектов Promise в переменных и последующего их ожидания. Давайте посмотрим на то, как это реализуется.

В нашем распоряжении имеется два примера — slow-async-await.html (см. исходный код) и fast-async-await.html (см. исходный код). Оба примера начинаются с функции-обещания, которая имитирует асинхронную операцию с помощью setTimeout():

function timeoutPromise(interval){
    return new Promise((resolve, reject) => {
        setTimeout(function(){
            resolve('done')
        }, interval)
    })
}

Затем следует асинхронная функция timeTest(), которая ожидает трех вызовов timeoutPromise():

async function timeTest(){
    ...
}

Каждый из трех вызовов timeTest() завершается записью времени выполнения обещания, затем записывается время выполнения всей операции:

let startTime = Date.now()
timeTest().then(() => {
    let finishTime = Date.now()
    let timeTaken = finishTime - startTime
    alert('Time taken in milliseconds: ' + timeTaken)
})

В каждом случае функция timeTest() отличается.

В slow-async-await.html timeTest() выглядит так:

async function timeTest(){
    await timeoutPromise(3000)
    await timeoutPromise(3000)
    await timeoutPromise(3000)
}

Здесь мы просто ожидаем три вызова timeoutPromise, каждый раз устанавливая задержку в 3 секунды. Каждый вызов ждет завершения предыдущего — если вы запустите первый пример, то увидите модальное окно примерно через 9 секунд.

В fast-async-await.html timeTest() выглядит так:

async function timeTest(){
    const timeoutPromise1 = timeoutPromise(3000)
    const timeoutPromise2 = timeoutPromise(3000)
    const timeoutPromise3 = timeoutPromise(3000)

    await timeoutPromise1
    await timeoutPromise2
    await timeoutPromise3
}

Здесь мы сохраняем три объекта Promise в переменных, что заставляет связанные с ним процессы выполняться одновременно.

Далее мы ожидаем их результаты — поскольку обещания начинают выполняться одновременно, обещания завершатся также в одно время; когда вы запустите второй пример, то увидите модальное окно примерно через 3 секунды!

Вам следует осторожно тестировать код и помнить об этом при снижении производительности.

Еще одним незначительным неудобством является необходимость оборачивания ожидаемых обещаний в асинхронную функцию.

Использование async/await совместно с классами

В завершение отметим, что вы можете добавлять async даже в методах создания классов, чтобы они возвращали обещания, и ждать обещания внутри них. Возьмем код из статьи про объектно-ориентированный JS и сравним его с модифицированной с помощью async версией:

class Person{
    constructor(first, last, age, gender, interests){
        this.name = {
            first,
            last
        }
        this.age = age
        this.gender = gender
        this.interests = interests
    }

    async greeting(){
        return await Promise.resolve(`Hi! I'm ${this.name.first}`)
    }

    farewell(){
        console.log(`${this.name.first} has left the building. Bye for now!`)
    }
}

let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling'])

Метод класса может быть использован следующим образом:

han.greeting().then(console.log)

Поддержка браузеров

Одним из препятствий использования async/await является отсутствие поддержки старых браузеров. Эта возможность доступна почти во всех современных браузерах, также как обещания; некоторые проблемы существуют в Internet Explorer и Opera Mini.

Если вы хотите использовать async/await, но нуждаетесь в поддержке старых браузеров, можете использовать библиотеку BabelJS — она позволяет использовать новейший JS, преобразуя его в подходящий для конкретного браузера.

Заключение

Async/await позволяет писать асинхронный код, который легко читать и поддерживать. Несмотря на то, что async/await поддерживается хуже других способов написания асинхронного кода, его определенно стоит изучить.

Благодарю за внимание.

Счастливого кодинга!

Начало: «JavaScript: простой пример работы с методом fetch».

В прошлом посте я разбирал, как заставить работать пример с встроенным методом fetch из подраздела 3.1 «Fetch» третьего раздела («Сетевые запросы») третьей части («Тематические разделы») учебника по JavaScript.

В итоге у меня получился такой код:

(async () => {
    let url = "https://api.github.com";
    let response = await fetch(url);

    if (response.ok) { // если HTTP-статус в диапазоне 200-299
        // получаем тело ответа
        let json = await response.json();
        console.log(json);
    } else {
        alert("Ошибка HTTP: " + response.status);
    }
})();

Можно было заметить, что, несмотря на вроде бы наличие ветки с обработкой ошибок, скрипт всё равно при некоторых ошибках (эти ошибки в консоли разработчика содержат в своём названии слово «Uncaught», что по-русски означает «Непойманная» [в смысле «непойманная ошибка»]) прекращает работу. (Я писал об этом отдельный пост: «JavaScript: слово Uncaught в названии ошибки».)

На самом деле, в вышеприведенном коде ветка с обработкой ошибок обрабатывает только ошибки, которые возникнут на веб-сервере при обработке запроса, который мы в данном коде посылаем на веб-сервер (ошибкой наш скрипт считает все ответы веб-сервера с кодом, выходящим за пределы диапазона 200-299).

Ну а непойманная ошибка в данном случае может возникнуть до того, как исполнение скрипта дойдет до ветки с обработкой ошибок, при выполнении одной из двух асинхронных задач. Либо тут (выполнение первой асинхронной задачи):

    let response = await fetch(url);

либо тут (выполнение второй асинхронной задачи):

        let json = await response.json();

Если случается непойманная скриптом ошибка, скрипт прекращает работу. При обучении или отладке мы временно можем себе это позволить, так как у нас при этом есть более важные задачи: при обучении более важная задача — это понять иллюстрируемые данным примером кода некие принципы программирования или решение каких-то задач (например, научиться работать с встроенным методом fetch, как в обсуждаемой статье учебника); а при отладке более важная задача — заставить код сначала хоть как-то работать.

Но в скрипте, как окончательном продукте, мы не можем себе позволить непойманных ошибок. Ведь в скрипте при возникновении непойманной ошибки могут еще быть невыполненные инструкции, то есть скрипт на момент такой ошибки, возможно, еще не успел сделать какие-то важные действия. Пользователю при этом никаких ошибок не показывается, скрипт просто «умирает» (если пользователь продвинутый, он, конечно, может открыть консоль разработчика в браузере [если скрипт был запущен в браузерном окружении] и увидеть сообщение о непойманной ошибке). Так что все возможные непойманные ошибки должны быть отловлены.

Как это сделать в данном случае? Изменим код (я отметил изменение красным цветом):

(async () => {
    let url = "https://api.github.com";
    let response = await fetch(url);

    if (response.ok) { // если HTTP-статус в диапазоне 200-299
        // получаем тело ответа
        let json = await response.json();
        console.log(json);
    } else {
        alert("Ошибка HTTP: " + response.status);
    }
})().catch(error => alert(error));

Этот метод catch в данном случае отловит ошибку при выполнении любой из двух асинхронных задач. Естественно, вместо простого вывода сообщения об ошибке на экран с помощью встроенной функции alert, можно написать какую-то более сложную обработку ошибки.

Подробнее об обработке ошибок при работе с промисами можно почитать следующие статьи учебника из первой части учебника:

Потребители: then, catch, finally
https://learn.javascript.ru/promise-basics#potrebiteli-then-catch-finally

Промисы: обработка ошибок
https://learn.javascript.ru/promise-error-handling

Обработка ошибок при использовании синтаксиса «async/await»
https://learn.javascript.ru/async-await#obrabotka-oshibok

Cover image for When That's Not So Fetch: Error Handling With fetch()

The fetch() method in JavaScript is a global, asynchronous method that allows us to interface with API’s for requests and responses. While this is a powerful and commonly-used tool, its error handling process may seem a bit elusive at start.

Why Error Handling?

When errors are encountered during a fetch() call, it is often necessary to halt the compiler from reading the next few lines of code. To do so, the method call should throw an error upon encountering one. The thrown error can be «caught» later on for an alternate behavior to take place. Although one might think that the fetch() call would automatically throw an error upon encountering one, that is not the case for JavaScript.

According to the fetch() MDN, the Promise object returned by the fetch() call is rejected (throws an error) only when «a network error is encountered.» This means that fetch() Promises do resolve despite encountering client-side HTTP errors such as 404 and do not throw errors during the fetch. Therefore, the code shown below would log «Success» instead of «Error» when run, which may seem unexpected.

fetch(url) // encounters a 404 error
   .then(res => res.json()) // no error is thrown
   .then(() => console.log("Success")) // 
   .catch(() => console.log("Error")) // fails to catch error

Enter fullscreen mode

Exit fullscreen mode

Luckily, you can fix this quite simply by using proper error handling.

Handling fetch() Errors

fetch() calls can be made using either Promise chains or Async/Await. Fortunately, the error handling process is similar for both.

Using Promises

The fetch API provides an ok property to the Promise response which indicates whether the HTTP status is within the range 200-299 (inclusive). This can be used to check whether any error is encountered during fetch.

const handleError = response => {
   if (!response.ok) { 
      throw Error(response.statusText);
   } else {
      return response.json();
   }
}; //handler function that throws any encountered error

fetch(url)
   .then(handleError) // skips to .catch if error is thrown
   .then(data => console.log("Does something with data"))
   .catch(console.log); // catches the error and logs it

Enter fullscreen mode

Exit fullscreen mode

The error-handler function should be called before the Promise response is parsed by .json(). Otherwise, the .json() method would strip out the response properties necessary for error handling (such as ok, status, and statusText).

Using Async/Await

Error handling using Async/Await uses a slightly different syntax, but it also revolves around the idea of using the ok property to check whether any error is encountered or not.

const response = await fetch(url);
if (!response.ok) {
   console.log(response.status, response.statusText);
} else {
   const data = await response.json();
   console.log(data);
}

Enter fullscreen mode

Exit fullscreen mode

The status response property provides the status code (e.g. «404») while the statusText response property provides the status description (e.g. «Is Not Found»).

Conclusion

Although the error handling for fetch() may not seem intuitive at first, it will later make more sense since it provides the user with more control over unique situations.

Overall, error-handling for fetch() calls is a simple and user-friendly tool that will definitely aid you in the long-term.

Resources

  • Handling Failed HTTP Responses With fetch()
  • Error handling while using native fetch API in JavaScript
  • Using Fetch MDN

  • Festool ka 65 ошибка 9
  • Ferrum tm 8420a ошибка е01
  • Ferroli ошибка а01 а 01 на газовом котле
  • Ferroli ошибка f35 как исправить
  • Ferroli котел ошибка а06