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
Время на прочтение
4 мин
Количество просмотров 11K
Если вы пришли сюда только ради ответа и вам не интересны рассуждения — листайте вниз
Как все начиналось
Для начала, давайте вспомним, а как вообще ловят ошибки в js, будь то браузер или сервер. В js есть конструкция try...catch
.
try {
let data = JSON.parse('...');
} catch(err: any) {
// если произойдет ошибка, то мы окажемся здесь
}
Это общепринятая конструкция и в большинстве языков она есть. Однако, тут есть проблема (и как окажется дальше — не единственная), эта конструкция «не будет работать» для асинхронного кода, для кода который был лет 5 назад. В те времена, в браузере использовали для Ajax запроса XMLHttpRequest
.
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com', true);
xhr.addEventListener('error', (e: ProgressEvent<XMLHttpRequestEventTarget>) => {
// если произойдет ошибка, то мы окажемся здесь
});
Тут используется механизм подписки на событие возникновения ошибки. В данном случае, переменная e
является событием, фактически мы ушли от настоящей ошибки и закрылись некоторой абстракцией, за которой спрятана настоящая ошибка, доступа к которой у нас нет.
В NodeJS с самого начала продвигалась концепция Error-First Callback, эта идея применялась для асинхронных функций, например, для чтения файла. Смысл ее в том, чтобы первым аргументом передавать в функцию обратного вызова ошибку, а следующими аргументами уже получаемые данные.
import fs from 'fs';
fs.readFile('file.txt', (err, data) => {
if (err) {
// обработка ошибки
}
// если все хорошо, работаем с данными
});
Если мы посмотрим какой тип имеет переменная err
, то увидим следующее:
interface ErrnoException extends Error {
errno?: number | undefined;
code?: string | undefined;
path?: string | undefined;
syscall?: string | undefined;
}
Тут действительно находится ошибка. По сути, это тот же способ, что и выше, только в этом случает мы получаем объект Error
.
Через некоторое время, в Javascript появились Promise
. Они, безусловно, изменили разработку на js к лучшему. Ведь никто* никто не любит городить огромные конструкции из функций обратного вызова.
fetch('https://api.example.com')
.then(res => {
// если все хорошо, работаем с данными
})
.catch(err => {
// обработка ошибки
});
Несмотря на то, что внешне этот пример сильно отличается от первого, тем не менее, мы видим явную логическую связь. Очевидно, что разработчики хотели сделать похожую на try...catch
конструкцию. Со временем, появился еще один способ обработать ошибку в асинхронном коде. Этот способ, по сути, является лишь синтаксическим сахаром для предыдущего примера.
try {
const res = await fetch('https://api.example.com');
// если все хорошо, работаем с данными
} catch(err) {
// обработка ошибки
}
Также, конструкция try...catch
позволяет ловить ошибки из нескольких промисов одновременно.
try {
let usersRes = await fetch('https://api.example.com/users');
let users = await usersRes.json();
let chatsRes = await fetch('https://api.example.com/chats');
let chats = await chatsRes.json();
// если все хорошо, работаем с данными
} catch(err) {
// обработка ошибки
}
Вот, замечательный вариант ловли ошибок. Любая ошибка которая возникнет внутри блока try
, попадет в блок catch
и мы точно её обработаем.
А точно ли обработаем?
Действительно, а правда ли, что мы обработаем ошибку, или всего лишь сделаем вид? На практике, скорее всего, возникнувшая ошибка будет просто выведена в консоль или т.п. Более того, при появлении ошибки*, интерпретатор прыгнет в блок catch
, где не мы, не TypeScript не сможет вывести тип переменной, попавшей туда (пример — возврат с помощью Promise.reject
), после чего, произойдет выход из функции. То есть, мы не сможем выполнить код который находится в этом же блоке, но который расположен ниже функции, внутри которой произошла ошибка. Конечно, мы можем предусмотреть такие ситуации, но сложность кода и читаемость вырастут многократно.
Как быть?
Давайте попробуем использовать подход, предлагаемый разработчиками одного небезызвестного языка.
let [users, err] = await httpGET('https://api.example.com/users');
if (err !== null) {
// обработка ошибки
}
// продолжаем выполнение кода
Возможную ошибку мы держим всегда рядом с данными, возвращаемыми из функции, что намекает нам на то, что переменную err
желательно проверить.
Пример для вызова нескольких функций возвращающих Promise
.
let err: Error,
users: User[],
chats: Chat[];
[users, err] = await httpGET('https://api.example.com/users');
if (err !== nil) {
// обработка ошибки
}
[chats, err] = await httpGET('https://api.example.com/chats');
if (err !== nil) {
// обработка ошибки
}
Конечно, мы можем, как и прежде, просто выходить из функций при появлении ошибки, но если, все таки, появляется необходимость отнестись к коду более ответственно, мы без труда можем начать это делать.
Давайте рассмотрим как можно реализовать такую функцию и что нам вообще нужно делать. Для начала, давайте определим тип PairPromise
. В данном случае, я решил использовать null
если результата или ошибки нету, так как он просто короче.
type PairPromise<T> = Promise<[T, null] | [null, Error]>;
Определим возможные возвращаемые ошибки.
const notFoundError = new Error('NOT_FOUND');
const serviceUnavailable = new Error('SERVICE_UNAVAILABLE');
Теперь опишем нашу функцию.
const getUsers = async (): PairPromise<User[]> => {
try {
let res = await fetch('https://api.example.com/users');
if (res.status === 504) {
return Promise.resolve([null, serviceUnavailable]);
}
let users = await res.json() as User[];
if (users.length === 0) {
return Promise.resolve([null, notFoundError]);
}
return Promise.resolve([users, null]);
} catch(err) {
return Promise.resolve([null, err]);
}
}
Пример использования такой функции.
let [users, err] = await getUsers();
if (err !== null) {
switch (err) {
case serviceUnavailable:
// сервис недоступен
case notFoundError:
// пользователи не найдены
default:
// действие при неизвестной ошибке
}
}
Вариантов применения данного подхода обработки ошибок очень много. Мы сочетаем удобства конструкции try...catch
и Error-First Callback, мы гарантированно поймаем все ошибки и сможем удобно их обработать, при необходимости. Как приятный бонус — мы не теряем типизацию. Также, мы не скованы лишь объектом Error
, мы можем возвращать свои обертки и успешно их использовать, в зависимости от наших убеждений.
Очень интересно мнение сообщества на эту тему.
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()
.
В статье мы разобрали, как использовать эту комбинацию для получения данных, обработки ошибок, отмены запросов и выполнения параллельных запросов.
Начало: «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
May 23, 2022
- Anticipate potential network errors
- Examples of user errors
- Examples of environmental changes
- Examples of errors with the video-sharing website
- Handle errors with the Fetch API
- When the Fetch API throws errors
- When the network status code represents an error
- When there is an error parsing the network response
- When the network request must be canceled before it completes
- Conclusion
This article demonstrates some error handling approaches when working with the Fetch API. The Fetch API lets you make a request to a remote network resource. When you make a remote network call, your web page becomes subject to a variety of potential network errors.
The following sections describe potential errors and describe how to write code that provides a sensible level of functionality that is resilient to errors and unexpected network conditions. Resilient code keeps your users happy and maintains a standard level of service for your website.
Anticipate potential network errors #
This section describes a scenario in which the user creates a new video named "My Travels.mp4"
and then attempts to upload the video to a video-sharing website.
When working with Fetch, it’s easy to consider the happy path where the user successfully uploads the video. However, there are other paths that are not as smooth, but for which web developers must plan. Such (unhappy) paths can happen due to user error, through unexpected environmental conditions, or because of a bug on the video-sharing website.
Examples of user errors #
- The user uploads an image file (such as JPEG) instead of a video file.
- The user begins uploading the wrong video file. Then, part way through the upload, the user specifies the correct video file for upload.
- The user accidentally clicks «Cancel upload» while the video is uploading.
Examples of environmental changes #
- The internet connection goes offline while the video is uploading.
- The browser restarts while the video is uploading.
- The servers for the video-sharing website restart while the video is uploading.
Examples of errors with the video-sharing website #
- The video-sharing website cannot handle a filename with a space. Instead of
"My Travels.mp4"
, it expects a name such as"My_Travels.mp4"
or"MyTravels.mp4"
. - The video-sharing website cannot upload a video that exceeds the maximum acceptable file size.
- The video-sharing website does not support the video codec in the uploaded video.
These examples can and do happen in the real world. You may have encountered such examples in the past! Let’s pick one example from each of the previous categories, and discuss the following points:
- What is the default behavior if the video-sharing service cannot handle the given example?
- What does the user expect to happen in the example?
- How can we improve the process?
Action | The user begins uploading the wrong video file. Then, part way through the upload, the user specifies the correct video file for upload. |
---|---|
What happens by default | The original file continues to upload in the background while the new file uploads at the same time. |
What the user expects | The user expects the original upload to stop so that no extra internet bandwidth is wasted. |
What can be improved | JavaScript cancels the Fetch request for the original file before the new file begins to upload. |
Action | The user loses their internet connection part way through uploading the video. |
---|---|
What happens by default | The upload progress bar appears to be stuck on 50%. Eventually, the Fetch API experiences a timeout and the uploaded data is discarded. When internet connectivity returns, the user has to reupload their file. |
What the user expects | The user expects to be notified when their file cannot be uploaded, and they expect their upload to automatically resume at 50% when they are back online. |
What can be improved | The upload page informs the user of internet connectivity issues, and reassures the user that the upload will resume when internet connectivity has resumed. |
Action | The video-sharing website cannot handle a filename with a space. Instead of «My Travels.mp4», it expects names such as «My_Travels.mp4» or «MyTravels.mp4». |
---|---|
What happens by default | The user must wait for the upload to completely finish. Once the file is uploaded, and the progress bar reads «100%», the progress bar displays the message: «Please try again.» |
What the user expects | The user expects to be told of filename limitations before upload begins, or at least within the first second of uploading. |
What can be improved | Ideally, the video-sharing service supports filenames with spaces. Alternative options are to notify the user of filename limitations before uploading begins. Or, the video-sharing service should reject the upload with a detailed error message. |
Handle errors with the Fetch API #
Note that the following code examples use top-level await
(browser support) because this feature can simplify your code.
When the Fetch API throws errors #
This example uses a try
/catch
block statement to catch any errors thrown within the try
block. For example, if the Fetch API cannot fetch the specified resource, then an error is thrown. Within a catch
block like this, take care to provide a meaningful user experience. If a spinner, a common user interface that represents some sort of progress, is shown to the user, then you could take the following actions within a catch
block:
- Remove the spinner from the page.
- Provide helpful messaging that explains what went wrong, and what options the user can take.
- Based on the available options, present a «Try again» button to the user.
- Behind the scenes, send the details of the error to your error-tracking service, or to the back-end. This action logs the error so it can be diagnosed at a later stage.
try {
const response = await fetch('https://website');
} catch (error) {
// TypeError: Failed to fetch
console.log('There was an error', error);
}
At a later stage, while you diagnose the error that you logged, you can write a test case to catch such an error before your users are aware something is wrong. Depending on the error, the test could be a unit, integration, or acceptance test.
When the network status code represents an error #
This code example makes a request to an HTTP testing service that always responds with the HTTP status code 429 Too Many Requests
. Interestingly, the response does not reach the catch
block. A 404 status, amongst certain other status codes, does not return a network error but instead resolves normally.
To check that the HTTP status code was successful, you can use any of the following options:
- Use the
Response.ok
property to determine whether the status code was in the range from200
to299
. - Use the
Response.status
property to determine whether the response was successful. - Use any other metadata, such as
Response.headers
, to assess whether the response was successful.
let response;try {
response = await fetch('https://httpbin.org/status/429');
} catch (error) {
console.log('There was an error', error);
}
// Uses the 'optional chaining' operator
if (response?.ok) {
console.log('Use the response here!');
} else {
console.log(`HTTP Response Code: ${response?.status}`)
}
The best practice is to work with people in your organization and team to understand potential HTTP response status codes. Backend developers, developer operations, and service engineers can sometimes provide unique insight into possible edge cases that you might not anticipate.
When there is an error parsing the network response #
This code example demonstrates another type of error that can arise with parsing a response body. The Response
interface offers convenient methods to parse different types of data, such as text or JSON. In the following code, a network request is made to an HTTP testing service that returns an HTML string as the response body. However, an attempt is made to parse the response body as JSON, throwing an error.
let json;try {
const response = await fetch('https://httpbin.org/html');
json = await response.json();
} catch (error) {
if (error instanceof SyntaxError) {
// Unexpected token < in JSON
console.log('There was a SyntaxError', error);
} else {
console.log('There was an error', error);
}
}
if (json) {
console.log('Use the JSON here!', json);
}
You must prepare your code to take in a variety of response formats, and verify that an unexpected response doesn’t break the web page for the user.
Consider the following scenario: You have a remote resource that returns a valid JSON response, and it is parsed successfully with the Response.json()
method. It may happen that the service goes down. Once down, a 500 Internal Server Error
is returned. If appropriate error-handling techniques are not used during the parsing of JSON, this could break the page for the user because an unhandled error is thrown.
When the network request must be canceled before it completes #
This code example uses an AbortController
to cancel an in-flight request. An in-flight request is a network request that has started but has not completed.
The scenarios where you may need to cancel an in-flight request can vary, but it ultimately depends on your use case and environment. The following code demonstrates how to pass an AbortSignal
to the Fetch API. The AbortSignal
is attached to an AbortController
, and the AbortController
includes an abort()
method, which signifies to the browser that the network request should be canceled.
const controller = new AbortController();
const signal = controller.signal;// Cancel the fetch request in 500ms
setTimeout(() => controller.abort(), 500);
try {
const url = 'https://httpbin.org/delay/1';
const response = await fetch(url, { signal });
console.log(response);
} catch (error) {
// DOMException: The user aborted a request.
console.log('Error: ', error)
}
Conclusion #
One important aspect of handling errors is to define the various parts that can go wrong. For each scenario, make sure you have an appropriate fallback in place for the user. With regards to a fetch request, ask yourself questions such as:
- What happens if the target server goes down?
- What happens if Fetch receives an unexpected response?
- What happens if the user’s internet connection fails?
Depending on the complexity of your web page, you can also sketch out a flowchart which describes the functionality and user interface for different scenarios.
Return to all articles