Json parse обработка ошибок

I’m using JSON.parse on a response that sometimes contains a 404 response. In the cases where it returns 404, is there a way to catch an exception and then execute some other code?

data = JSON.parse(response, function (key, value) {
    var type;
    if (value && typeof value === 'object') {
        type = value.type;
        if (typeof type === 'string' && typeof window[type] === 'function') {
            return new(window[type])(value);
        }
    }
    return value;
});

Sebastian Simon's user avatar

asked Dec 17, 2010 at 1:52

prostock's user avatar

3

i post something into an iframe then read back the contents of the iframe with json parse…so sometimes it’s not a json string

Try this:

if (response) {
    let a;
    try {
        a = JSON.parse(response);
    } catch (e) {
        return console.error(e); // error in the above string (in this case, yes)!
    }
    // if no error, we can now keep using "a"
}

Mike 'Pomax' Kamermans's user avatar

answered Dec 17, 2010 at 3:08

UltraInstinct's user avatar

UltraInstinctUltraInstinct

43.3k12 gold badges81 silver badges104 bronze badges

4

We can check error & 404 statusCode, and use try {} catch (err) {}.

You can try this :

const req = new XMLHttpRequest();
req.onreadystatechange = function() {
    if (req.status == 404) {
        console.log("404");
        return false;
    }

    if (!(req.readyState == 4 && req.status == 200))
        return false;

    const json = (function(raw) {
        try {
            return JSON.parse(raw);
        } catch (err) {
            return false;
        }
    })(req.responseText);

    if (!json)
        return false;

    document.body.innerHTML = "Your city : " + json.city + "<br>Your isp : " + json.org;
};
req.open("GET", "https://ipapi.co/json/", true);
req.send();

Read more :

  • Catch a 404 error for XHR

answered Jul 13, 2017 at 13:45

user2226755's user avatar

user2226755user2226755

12.5k5 gold badges50 silver badges73 bronze badges

I am fairly new to Javascript. But this is what I understood:
JSON.parse() returns SyntaxError exceptions when invalid JSON is provided as its first parameter. So. It would be better to catch that exception as such like as follows:

try {
    let sData = `
        {
            "id": "1",
            "name": "UbuntuGod",
        }
    `;
    console.log(JSON.parse(sData));
} catch (objError) {
    if (objError instanceof SyntaxError) {
        console.error(objError.name);
    } else {
        console.error(objError.message);
    }
}

The reason why I made the words «first parameter» bold is that JSON.parse() takes a reviver function as its second parameter.

answered Jul 21, 2018 at 3:15

ubuntugod's user avatar

ubuntugodubuntugod

6028 silver badges16 bronze badges

3

if you are looking for a generalized function for this, give this a shot.

const parseJSON = (inputString, fallback) => {
  if (inputString) {
    try {
      return JSON.parse(inputString);
    } catch (e) {
      return fallback;
    }
  }
};

answered Jan 21, 2022 at 7:56

Kartik Malik's user avatar

I recommend you to use this one as a ES6 best practice. Using Error object

try {
  myResponse = JSON.parse(response);
} catch (e) {
  throw new Error('Error occured: ', e);
 }

Above answer also helpful,

answered Feb 28, 2022 at 8:52

Bushra Mustofa's user avatar

1

Just for my own fun, I tried to create oneliners out of 2 answers from here:

let response = '{"a": 123}';
let data = (_=> {try { return JSON.parse(response); } catch(err) { return undefined; }})();
if (!data) ...404...

NOTE: return undefined; is just for completeness, as return; will also result in undefined.

let response = '{"a": 123}';
let data = await Promise.resolve(_ => JSON.parse(response)).then(f => f()).catch(err => undefined);
if (!data) ...404...

NOTE: Yeah, I don’t know .. the benefits of running a function inside of a Promise just to catch its error … seems kind of wrong to me … but non the less an interesting idea.

Please feel free and share your thoughts on those in the comments.

answered Jul 9 at 3:45

BananaAcid's user avatar

BananaAcidBananaAcid

3,24136 silver badges38 bronze badges

You can try this:

Promise.resolve(JSON.parse(response)).then(json => {
    response = json ;
}).catch(err => {
    response = response
});

dereli's user avatar

dereli

1,81415 silver badges23 bronze badges

answered Mar 5, 2019 at 5:36

vivek java's user avatar

This promise will not resolve if the argument of JSON.parse() can not be parsed into a JSON object.

Promise.resolve(JSON.parse('{"key":"value"}')).then(json => {
    console.log(json);
}).catch(err => {
    console.log(err);
});

answered Oct 5, 2017 at 7:32

Jason Clay's user avatar

3

JSON ( JavaScript Object Notation), is widely used format for asynchronous communication between webpage or mobile application to back-end servers. Due to increasing trend in Single Page Application or Mobile Application, popularity of the JSON is extreme.

Why do we get JSON parse error?

Parsing JSON is a very common task in JavaScript. JSON.parse() is a built-in method in JavaScript which is used to parse a JSON string and convert it into a JavaScript object. If the JSON string is invalid, it will throw a SyntaxError.

const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);
console.log(obj.count);
// expected output: 42
console.log(obj.result);
// expected output: true

How to handle JSON parse error?

There are many ways to handle JSON parse error. In this post, I will show you how to handle JSON parse error in JavaScript.

1. Using try-catch block

The most common way to handle JSON parse error is using try-catch block. If the JSON string is valid, it will return a JavaScript object. If the JSON string is invalid, it will throw a SyntaxError.

try {
  const json = '{"result":true, "count":42}';
  const obj = JSON.parse(json);
  console.log(obj.count);
  // expected output: 42
  console.log(obj.result);
  // expected output: true
} catch (e) {
  console.log(e);
  // expected output: SyntaxError: Unexpected token o in JSON at position 1
}

2. Using if-else block

Another way to handle JSON parse error is using if-else block.

const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);
if (obj instanceof SyntaxError) {
  console.log(obj);
  // expected output: SyntaxError: Unexpected token o in JSON at position 1
} else {
  console.log(obj.count);
  // expected output: 42
  console.log(obj.result);
  // expected output: true
}

3. Using try-catch block with JSON.parse()

The third way to handle JSON parse error is using try-catch block with JSON.parse().

const json = '{"result":true, "count":42}';
const obj = JSON.parse(json, (key, value) => {
  try {
    return JSON.parse(value);
  } catch (e) {
    return value;
  }
});
console.log(obj.count);
// expected output: 42
console.log(obj.result);
// expected output: true

4. Using try-catch block with JSON.parse() and JSON.stringify()

The fourth way to handle JSON parse error is using try-catch block with JSON.parse() and JSON.stringify(). If the JSON string is valid, it will return a JavaScript object. If the JSON string is invalid, it will return a SyntaxError.

const json = '{"result":true, "count":42}';
const obj = JSON.parse(json, (key, value) => {
  try {
    return JSON.parse(value);
  } catch (e) {
    return value;
  }
});
const str = JSON.stringify(obj);
console.log(str);
// expected output: {"result":true,"count":42}

The best way to catch invalid JSON parsing errors is to put the calls to JSON.parse() to a try/catch block.

You really do not have any other option — the built-in implementation throws an exception on invalid JSON data and the only way to prevent that exception from halting your application is to catch it. Even using a 3rd party library will not avoid that — they must do a try/catch on a JSON.parse() call somewhere.

The only alternative is to implement your own JSON parsing algorithm that could be more forgiving on invalid data structures, but that feels like digging a 1 cubic metre hole with a small nuke.

Note about performance

The v8 JavaScript engine used by Node.js cannot optimise functions which contain a try/catch block.

Update: v8 4.5 and above can optimise try/catch. For older releases, see below.

A simple workaround is to put the safe-parsing logic into a separate function so that the main function can still be optimised:

function safelyParseJSON (json) {
  // This function cannot be optimised, it's best to
  // keep it small!
  var parsed

  try {
    parsed = JSON.parse(json)
  } catch (e) {
    // Oh well, but whatever...
  }

  return parsed // Could be undefined!
}

function doAlotOfStuff () {
  // ... stuff stuff stuff
  var json = safelyParseJSON(data)
  // Tadaa, I just got rid of an optimisation killer!
}

If the JSON parsing is done sporadically, this might not have a noticeable effect on performance, but if used improperly in usage-heavy function it could lead to dramatic increase in response times.

Note about try/catch being blocking

It should be noted that every.single.statement of JavaScript code in Node.js is executed only one-at-a-time, no matter if it’s called from the main function or from a callback or from a different module or whatever. As such, every single statement will block the process. This is not necessarily a bad thing — a well designed application will spend most of its time waiting for an external resource (database response, HTTP communication, filesystem operations etc.). It is therefore of great importance that frequently executed JavaScript code can be optimised by the v8 engine so it takes as little time as possible in this blocked state — see the note about performance.

Ошибки есть в каждом коде. Мы перевели гайд разработчика Айо Исайя, в котором он рассказывает о системе ошибок и о том, как их устранять.

Раз вы читаете эту статью, вы, конечно, знакомы с концепцией ошибок в программировании. Это ошибки в коде, они же баги, которые приводят к сбою или неожиданному поведению программы. В отличие от некоторых языков, таких как Go и Rust, где вы вынуждены взаимодействовать с потенциальными ошибками на каждом этапе пути, в JavaScript и Node.js можно обойтись без согласованной стратегии обработки ошибок.

Однако именно такая стратегия делает жизнь проще. Цель статьи — познакомить вас с этими шаблонами для создания, доставки и обработки потенциальных ошибок. Шаблоны помогут обнаружить и обработать потенциальные ошибки в коде до развёртывания.

Что такое ошибки в Node.js

Ошибка в Node.js — это любой экземпляр объекта Error. Общие примеры включают встроенные классы ошибок: ReferenceError, RangeError, TypeError, URIError, EvalError и SyntaxError. Пользовательские ошибки также можно создать путём расширения базового объекта Error, встроенного класса ошибки или другой настраиваемой ошибки. При создании ошибок таким путём нужно передать строку сообщения, описывающую ошибку. К сообщению можно получить доступ через свойство message объекта. Объект Error также содержит свойства name и stack, которые указывают имя ошибки и точку в коде, в которой объект создаётся.

const userError = new TypeError("Something happened!");
console.log(userError.name); // TypeError
console.log(userError.message); // Something happened!
console.log(userError.stack);
/*TypeError: Something happened!
    at Object.<anonymous> (/home/ayo/dev/demo/main.js:2:19)
    <truncated for brevity>
    at node:internal/main/run_main_module:17:47 */

Функции объекта Error можно передать или вернуть из функции. Если бросить его с помощью throw, объект Error станет исключением. Когда вы передаёте ошибку из функции, она переходит вверх по стеку, пока исключение не будет поймано. В противном случае uncaught exception может обвалить всю работу.

Как обработать ошибку

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

Исключения

Чаще всего ошибки функций обрабатывают путём генерации. В этом случае ошибка становится исключением, после чего её можно поймать где-нибудь в стеке с помощью блока try / catch. Если у ошибки есть разрешение всплывать в стеке, не будучи перехваченной, она преобразуется в формат uncaughtException, что приводит к преждевременному завершению работы приложения. Например, встроенный метод JSON.parse () выдаёт ошибку, если строковый аргумент не является допустимым объектом JSON.

function parseJSON(data) {
  return JSON.parse(data);
}
try {
  const result = parseJSON('A string');
} catch (err) {
  console.log(err.message); // Unexpected token A in JSON at position 0
}

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

function square(num) {
  if (typeof num !== 'number') {
    throw new TypeError(`Expected number but got: ${typeof num}`);
  }
  return num * num;
}
try {
  square('8');
} catch (err) {
  console.log(err.message); // Expected number but got: string
}

Колбэк с первым аргументом-ошибкой

Из-за своей асинхронной природы Node.js интенсивно использует функции колбэка для обработки большей части ошибок. Колбэк (обратный вызов) передаётся в качестве аргумента другой функции и выполняется, когда последняя завершает свою работу.

Node.js использует колбэк с первым аргументом-ошибкой в большинстве асинхронных методов, чтобы гарантировать проверку ошибок до результатов операции. Колбэк обычно является последним аргументом функции, инициирующей асинхронную операцию, и вызывается один раз при возникновении ошибки или получении результата:

function (err, result) {}

Первый аргумент зарезервирован для объекта ошибки. Если ошибка возникает в ходе асинхронной операции, она доступна через аргумент err при неопределённом результате. Однако, если ошибки не возникает, err будет иметь значение null или undefined, а result будет содержать ожидаемый результат операции. Этот шаблон работает, если прочитать содержимое файла с помощью встроенного метода fs.readFile ():

const fs = require('fs');
fs.readFile('/path/to/file.txt', (err, result) => {
  if (err) {
    console.error(err);
    return;
  }
  // Log the file contents if no error
  console.log(result);
});

Метод readFile () использует колбэк в качестве своего последнего аргумента, который, в свою очередь, соответствует подписи функции «первая ошибка». В этом сценарии result включает в себя содержимое файла, который читается, если ошибки не возникает. В противном случае он определяется как undefined, а аргумент err заполняется объектом ошибки, содержащим информацию о проблеме: файл не найден или недостаточно полномочий.

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

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

function square(num, callback) {
  if (typeof callback !== 'function') {
    throw new TypeError(`Callback must be a function. Got: ${typeof callback}`);
  }
  // simulate async operation
  setTimeout(() => {
    if (typeof num !== 'number') {
      // if an error occurs, it is passed as the first argument to the callback
      callback(new TypeError(`Expected number but got: ${typeof num}`));
      return;
    }
    const result = num * num;
    // callback is invoked after the operation completes with the result
    callback(null, result);
  }, 100);
}

Любой вызывающий функцию square должен пройти через колбэк, чтобы получить доступ к нужному результату или ошибке.

Не нужно непосредственно обрабатывать ошибку в функции колбэка. Её можно распространить вверх по стеку, передав на другой колбэк. Но сначала убедитесь, что вы не генерируете исключение внутри функции. Асинхронное исключение невозможно отследить, потому что окружающий блок try / catch завершается до выполнения колбэка. Следовательно, исключение будет распространяться на вершину стека, что приведёт к завершению работы приложения. Исключение — когда обработчик зарегистрирован для process.on ('uncaughtException').

try {
  square('8', (err, result) => {
    if (err) {
      throw err; // not recommended
    }
    console.log(result);
  });
} catch (err) {
  // This won't work
  console.error("Caught error: ", err);
}

Отклонение обещаний

Обещания в JavaScript — это актуальный способ выполнения асинхронных операций в Node.js. Они предпочтительнее колбэков из-за лучшего потока, который соответствует современным способам анализа программ, особенно с шаблоном async / await. Любой API-интерфейс Node.js, использующий колбэки с ошибкой для асинхронной обработки ошибок, может быть преобразован в обещания с помощью встроенного метода util.promisify (). Например, заставить метод fs.readFile () использовать обещания можно так:

const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);

Переменная readFile — это версия fs.readFile () с обещаниями, в которой отклонения обещаний используются для сообщения об ошибках. Эти ошибки можно отследить, связав метод catch:

readFile('/path/to/file.txt')
  .then((result) => console.log(result))
  .catch((err) => console.error(err));

Также можно использовать обещанные API в функциях async. Так выглядит основной способ использования обещаний в современном JavaScript: в нём код читается как синхронный, и для обработки ошибок применяют знакомый механизм try / catch. Перед асинхронным запуском важно использовать await, чтобы обещание было выполнено или отклонено до того, как функция возобновит выполнение. При отклонении обещания выражение await выбрасывает отклонённое значение, которое впоследствии попадает в окружающий блок catch.

(async function callReadFile() {
  try {
    const result = await readFile('/path/to/file.txt');
    console.log(result);
  } catch (err) {
    console.error(err);
  }
})();

Обещанияможно использовать в асинхронных функциях, возвращая обещание из функции и помещая код функции в обратный вызов обещания. Если есть ошибка, её стоит отклонить (reject) с помощью объекта Error. В противном случае можно разрешить (resolve) обещание с результатом, чтобы оно было доступно в цепочке метода .then или напрямую как значение функции async при использовании async / await.

function square(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (typeof num !== 'number') {
        reject(new TypeError(`Expected number but got: ${typeof num}`));
      }
      const result = num * num;
      resolve(result);
    }, 100);
  });
}
square('8')
  .then((result) => console.log(result))
  .catch((err) => console.error(err));

Источники событий

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

const { EventEmitter } = require('events');
function emitCount() {
  const emitter = new EventEmitter();
  let count = 0;
  // Async operation
  const interval = setInterval(() => {
    count++;
    if (count % 4 == 0) {
      emitter.emit(
        'error',
        new Error(`Something went wrong on count: ${count}`)
      );
      return;
    }
    emitter.emit('success', count);
    if (count === 10) {
      clearInterval(interval);
      emitter.emit('end');
    }
  }, 1000);
  return emitter;
}

Функция emitCount () возвращает новый эмиттер событий, который сообщает об успешном исходе в асинхронной операции. Она увеличивает значение переменной count и каждую секунду генерирует событие успеха и событие ошибки, если значение count делится на 4. Когда count достигает 10, генерируется событие завершения. Этот шаблон позволяет передавать результаты по мере их поступления вместо ожидания завершения всей операции.

Вот как можно отслеживать и реагировать на каждое из событий, генерируемых функцией emitCount ():

const counter = emitCount();
counter.on('success', (count) => {
  console.log(`Count is: ${count}`);
});
counter.on('error', (err) => {
  console.error(err.message);
});
counter.on('end', () => {
  console.info('Counter has ended');
});

Функция колбэка для каждого прослушивателя событий выполняется независимо, как только событие генерируется. Событие ошибки (error) — это особый случай для Node.js, потому что при отсутствии прослушивателя процесс Node.js выходит из строя. Вы можете закомментировать прослушиватель событий ошибки выше и запустить программу, чтобы увидеть, что произойдёт.

Расширение объекта ошибки

Необходимо создавать собственные пользовательские классы ошибок, чтобы лучше отражать разные типы ошибок: класс ValidationError для ошибок, возникающих при проверке пользовательского ввода, класс DatabaseError для операций с базами данных, TimeoutError для операций, для которых истекло назначенное им время ожидания.

Пользовательские классы ошибок, расширяющие объект Error, сохранят основные свойства ошибки: сообщение (message), имя (name) и стек (stack). Но у них есть собственные свойства. ValidationError можно улучшить, добавив значимые свойства — часть ввода, вызвавшую ошибку.

Вот как можно расширить встроенный объект Error в Node.js:

class ApplicationError extends Error {
  constructor(message) {
    super(message);
    // name is set to the name of the class
    this.name = this.constructor.name;
  }
}
class ValidationError extends ApplicationError {
  constructor(message, cause) {
    super(message);
    this.cause = cause
  }
}

Класс ApplicationError — общая ошибка, а класс ValidationError представляет любую ошибку, возникающую при проверке ввода данных пользователем. Он наследуется от класса ApplicationError и дополняет его свойством cause для указания ввода, вызвавшего ошибку. Пользовательские классы ошибки можно использовать, как и обычные:

function validateInput(input) {
  if (!input) {
    throw new ValidationError('Only truthy inputs allowed', input);
  }
  return input;
}
try {
  validateInput(userJson);
} catch (err) {
  if (err instanceof ValidationError) {
    console.error(`Validation error: ${err.message}, caused by: ${err.cause}`);
    return;
  }
  console.error(`Other error: ${err.message}`);
}

Ключевое слово instanceof следует использовать для проверки конкретного типа ошибки. Не используйте имя ошибки для проверки типа, как в err.name === 'ValidationError': это не сработает, если ошибка получена из подкласса ValidationError.

Типы ошибок

Типы ошибок можно разделить на две основные категории: ошибки программиста и операционные проблемы. К первому типу можно отнести неудачные или неправильные аргументы функции, в то время как временные сбои при работе с внешними API однозначно относятся ко второй категории.

Операционные ошибки

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

  • Запрос API не выполняется по какой-либо причине (например, сервер не работает или превышен лимит скорости).

  • Соединение с базой данных потеряно, например, из-за неисправного сетевого соединения.

  • ОС не может выполнить запрос на открытие файла или запись в него.

  • Пользователь отправляет на сервер недопустимые данные: неверный номер телефона или адрес электронной почты.

Ошибки программиста

Ошибки программиста — это ошибки в логике или синтаксисе программы, которые можно исправить только путём изменения исходного кода. Ошибки этого типа невозможно обработать, потому что это недочёты в программе:

  • Синтаксические ошибки: незакрытая фигурная скобка.

  • Ошибки типа при попытке сделать что-то неправильное: выполнение операций с операндами несовпадающих типов.

  • Неверные параметры при вызове функции.

  • Ссылки на ошибки при неправильном написании имени переменной, функции или свойства.

  • Попытка получить доступ к местоположению за концом массива.

  • Неспособность обработать операционную ошибку.

Обработка операционных ошибок

Операционные ошибки в большинстве случаев предсказуемы. Их обработка — это рассмотрение вероятности неудачного завершения операции, возможных причин и последствий. Рассмотрим несколько стратегий обработки операционных ошибок в Node.js.

Сообщить об ошибке в стек

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

Повторить операцию

Сетевые запросы к внешним службам иногда могут завершаться ошибкой, даже если запрос полностью верен. Это случается из-за сбоя и неполадках сети или перегрузке сервера. Можно повторить запрос несколько раз, пока он не будет успешно завершён или пока не будет достигнуто максимальное количество повторных попыток. Первое, что нужно сделать, — это определить, уместно ли повторить запрос. Если исходный код состояния HTTP ответа — 500, 503 или 429, повторте запрос через некоторое время.

Проверьте, присутствует ли в ответе HTTP-заголовок Retry-After. Он указывает на точное время ожидания перед выполнением последующего запроса. Если его нет, необходимо отложить последующий запрос и постепенно увеличивать временной промежуток для каждой повторной попытки. Этот метод известен как стратегия экспоненциального отката. Нужно ещё определить максимальное время задержки и число запросов до отказа от дальнейших попыток.

Отправить ошибку клиенту

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

Прервать программу.

В случае неисправимых системных ошибок разумный выход — зарегистрировать ошибку и немедленно завершить работу программы. Если исключение невозможно исправить на уровне JavaScript, то, возможно, не получится корректно завершить работу сервера. Тогда нужен системный администратор, способный всё исправить.

Предотвращение ошибок программиста

Ошибки программиста сами по себе не могут быть обработаны, потому что их причина в коде или в логике. Однако ошибаться можно реже.

Принять TypeScript

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

Когда проект на TypeScript, такие ошибки, как undefined is not a function, синтаксические или ссылочные ошибки, исчезают из кодовой базы. Перенос на TypeScript можно выполнять постепенно. Для быстрой миграции есть инструмент ts-migrate.

Определить поведение для неверных параметров

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

При работе с неверными параметрами и определяйте их поведение, либо выдавая ошибку, либо возвращая специальное значение, такое как null, undefined или -1, когда проблема может быть решена локально. Первый вариант— это подход, используемый JSON.parse (), который выдаёт исключение SyntaxError, если строка для синтаксического анализа недействительна. Второй вариант — метод string.indexOf ()

Автоматизированное тестирование

Автоматизированные наборы тестов повышает вероятность исправления ошибок. Тесты помогают выяснить, как функция работает с нетипичными значениями. Для модульного тестирования подходят среды, такие как Jest или Mocha.

Неперехваченные исключения и необработанные отклонения обещаний

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

// unsafe
process.on('uncaughtException', (err) => {
  console.error(err);
});

Но неперехваченное исключение указывает на то, что приложение находится в неопределённом состоянии. Поэтому попытка возобновить работу в обычном режиме без восстановления после ошибки небезопасна и может привести к утечке памяти и зависанию сокетов. Лучше использовать обработчик uncaught Exception для очистки всех выделенных ресурсов, закрытия соединений и ведения лога ошибок для оценки перед завершением процесса.

// better
process.on('uncaughtException', (err) => {
  Honeybadger.notify(error); // log the error in a permanent storage
  // attempt a gracefully shutdown
  server.close(() => {
    process.exit(1); // then exit
  });
  // If a graceful shutdown is not achieved after 1 second,
  // shut down the process completely
  setTimeout(() => {
    process.abort(); // exit immediately and generate a core dump file
  }, 1000).unref()
});

Событие unhandledRejection генерируется, когда отклонённое обещание не обрабатывается блоком catch. В отличие от uncaughtException, эти события не вызывают немедленного сбоя приложения. Однако необработанные отклонения обещаний сейчас признаны устаревшими и могут немедленно завершить процесс в следующих релизах Node.js. Отслеживать необработанные отклонения обещаний можно с помощью прослушивателя событий unhandledRejection:

process.on('unhandledRejection', (reason, promise) => {
  Honeybadger.notify({
    message: 'Unhandled promise rejection',
    params: {
      promise,
      reason,
    },
  });
  server.close(() => {
    process.exit(1);
  });
  setTimeout(() => {
    process.abort();
  }, 1000).unref()
});

Серверы необходимо запускать с помощью диспетчера процессов, который автоматически перезапустит их в случае сбоя. Распространённый вариант — PM2, но для Linux существуют также systemd и upstart, а пользователи Docker могут использовать собственную политику перезапуска. По завершении всех процессов стабильное обслуживание будет восстановлено почти мгновенно, а у вас будт информация о неперехваченном исключении. Можно запутсить несколько процессов и применить балансировщик нагрузки для распределения входящих запросов. Это поможет предотвратить простои.

Централизованная отчётность об ошибках

Ни одна стратегия обработки ошибок не будет полной без надёжной стратегии ведения журнала ошибок. Когда происходит сбой, важно узаписать как можно больше информации о проблеме. Централизация логов позволяет оценить, что происходит в коде. 

Honeybadger предоставляет всё необходимое для отслеживания ошибок. Интегрируется так:

Установите пакет

Используйте npm для установки пакета:

$ npm install @honeybadger-io/js --save

Импортируйте библиотеку

Импортируйте библиотеку и настройте её с помощью ключа API, чтобы получать сообщения об ошибках:

const Honeybadger = require('@honeybadger-io/js');
Honeybadger.configure({
  apiKey: '[ YOUR API KEY HERE ]'
});
Сообщите об ошибках
Метоодом notify ():
try {
  // ...error producing code
} catch(error) {
  Honeybadger.notify(error);
}

Просмотрите полную документацию или ознакомьтесь с образцом Node.js / Express на GitHub.


Без обработки ошибок не бывает надёжного софта. 

Спасибо за внимание и удачного кода!

В этой статье мы познакомимся с инструкцией для обработки ошибок try...catch и throw для генерирования исключений.

Непойманные ошибки

Ошибке в коде могут возникать по разным причинам. Например, вы отправили запрос на сервер, а он дал сбой и прислал ответ, который привёл к неожиданным последствиям. Кроме этой, могут быть тысячи других, а также свои собственные.

Когда возникает ошибка, выполнение кода прекращается, и эта ошибка выводится в консоль:

const json = '{name:"Александр"}';
const person = JSON.parse(json); // Uncaught SyntaxError: Unexpected token n in JSON at position 1
console.log('Это сообщение мы не увидим!');

Непойманная ошибка в JavaScript

Выполнение этого примера остановится при парсинге строки JSON. В консоль будет выведена непойманная ошибка (uncaught error). Она так называется, потому что мы её не поймали (не обработали). Дальше код выполняться не будет и сообщение, которые мы выводим с помощью console.log() не отобразится.

try…catch

Обработка ошибок в JavaScript осуществляется с помощью try...catch.

try...catch – это специальный синтаксис, состоящий из 2 блоков кода:

try {
  // блок кода, в котором имеется вероятность возникновения ошибки
} catch(error) {
  // этот блок выполняется только в случае возникновения ошибки в блоке try
}

Первый блок идёт сразу после ключевого слова try. В этот блок мы помещаем часть кода, в котором есть вероятность возникновения ошибки.

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

Код, приведённый выше мы обернули в try...catch, а именно ту его часть, в котором может возникнуть ошибка:

const text = '{name:"Александр"}';
try {
  const person = JSON.parse(text); // Uncaught SyntaxError: Unexpected token n in JSON at position 1
} catch(error) {
  console.error(error);
  console.log(error.message);
}
console.log('Это сообщение мы увидим!');

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

Здесь в блоке try произойдет ошибка, так как в данном примере мы специально присвоили переменной text некорректную строку JSON. В catch эта ошибка будет присвоена параметру error, и в нём мы будем просто выводить эту ошибку в консоль с помощью console.error(). Таким образом она будет выведена также красным цветом, но без слова Uncaught, т.к. эта ошибка была поймана.

Ошибка – это объект и у него имеются следующие свойства:

  • message – описание ошибки;
  • name – тип ошибки, например, RangeError при указании значения выходящего за пределы диапазона;
  • stack – строка стека, которая используется в целях отладки; она позволяет узнать о том, что происходило в скрипте на момент возникновения ошибки.

В этом примере мы также написали инструкцию для вывода описание ошибки error.message в консоль с помощью console.log().

Пример функции для проверки корректности JSON:

const isValidJSON = (text) => {
  try {
    JSON.parse(text);
    return true;
  } catch {
    return false;
  }
}

Функция для проверки корректности JSON, написанная с использование инструкции try...catch

При вызове функции, сначала будет выполняться инструкция JSON.parse(text). Если ошибки не возникнет, то возвратится значение true. В противном случае, интерпретатор перейдёт в секцию catch. В итоге будет возвращено false. Кстати здесь catch записан без указания круглых скобок и параметра внутри них. Эта возможность была добавлена в язык, начиная с версии ECMAScript 2019.

Блок «finally»

В JavaScript возможны три формы инструкции try:

  • try...catch
  • try...finally
  • try...catch...finally

Блок finally выполняется всегда, независимо от того возникли ошибки в try или нет. Он выполняется после try, если ошибок не было, и после catch, если ошибки были. Секция finally не имеет параметров.

Пример с использованием finally:

let result = 0;
try {
  result = sum(10, 20);
  console.log('Это сообщение мы не увидим!');
} catch(error) {
  console.log(error.message);
} finally {
  console.log(result);
}

Пример инструкции try с finally в JavaScript

В этом примере произойдет ошибка в секции try, так как sum нигде не определена. После возникновения ошибки интерпретатор перейдём в catch. Здесь с помощью метода console.log() сообщение об ошибке будет выведено в консоль. Затем выполнится инструкция, находящаяся в блоке finally.

В JavaScript имеется также конструкция без catch:

try {
  // ...
} finally {
  // завершаем какие-то действия
}

Инструкция throw

В JavaScript имеется инструкция throw, которая позволяет генерировать ошибку.

Синтаксис инструкции throw:

throw expression;

Как правило, в качестве выражения обычно используют встроенный основной класс для ошибок Error или более конкретный, например: RangeError, ReferenceError, SyntaxError, TypeError, URIError или другой.

Создаём новый объект Error и выбрасываем его в качестве исключения:

throw new Error('Какое-то описание ошибки');

Пример генерирования синтаксической ошибки:

throw new SyntaxError('Описание ошибки');

В качестве выражения можно использовать не только объект ошибки, но и строки, числа, логические значения и другие величины. Но делать это не рекомендуется:

throw 'Значение не является числом';

При обнаружении оператора throw выполнение кода прекращается, и ошибка выбрасывается в консоль.

Например, создадим функцию, которая будет просто выбрасывать новую ошибку:

// создаём стрелочную функцию и присваиваем её переменной myFn
const myFn = () => {
  throw new Error('Описание ошибки');
}
// вызываем функцию
myFn();
console.log('Это сообщение мы не увидим в консоли!');

Генерируем ошибку с помощью throw

Для обработки ошибки обернём вызов функции в try...catch:

const myFn = () => {
  throw new Error('Описание ошибки');
}
try {
  myFn();
} catch(error) {
  console.error(error);
}
console.log('Это сообщение мы увидим в консоли!');

Обрабатываем ошибку в try...catch, сгенерированную с помощью throw

В этом примере вы увидите в консоли ошибку и дальше сообщение, которые мы выводим с помощью console.log(). То есть выполнение кода продолжится.

Кроме встроенных классов ошибок можно создать свои собственные, например, путем расширения Error:

class FormError extends Error {
  constructor(message) {
    super(message);
    this.name = 'FormError';
  }
}

Использование своего класса FormError для отображение ошибок формы:

<form novalidate>
  <input type="text" name="name" required>
  <input type="email" name="email" required>
  <button type="submit">Отправить</button>
</form>

<script>
  class FormError extends Error {
    constructor(message) {
      super(message);
      this.name = 'FormError';
    }
  }
  const elForm = document.querySelector('form');
  elForm.onsubmit = (e) => {
    e.preventDefault();
    elForm.querySelectorAll('input').forEach((el) => {
      if (!el.checkValidity()) {
        try {
          throw new FormError(`[name="${el.name}"] ${el.validationMessage}`);
        } catch(error) {
          console.error(`${error.name} ${error.message}`);
        }
      }
    });
  }
</script>

Создание собственного класса ошибок и использование его для отображение ошибок формы в JavaScript

Глобальная ловля ошибок

Возникновение ошибок, которые мы никак не обрабатываем с помощью try, можно очень просто перехватить посредством window.onerror:

window.onerror = function(message, source, lineno, colno, error) {
  // ...
}

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

  • message — строка, содержащее сообщение об ошибке;
  • source — URL-адрес скрипта или документа, в котором произошла ошибка;
  • lineno и colno — соответственно номер строки и столбца, в которой произошла ошибка;
  • error — объект ошибки или null, если соответствующий объект ошибки недоступен;

Передача ошибок на сервер

Что делать с этими ошибками? Их, например, можно передавать на сервер для того чтобы позже можно было проанализировать эти ошибки и принять меры по их устранению.

Пример кода для отправки ошибок, возникающих в браузере на сервер через AJAX с использованием fetch:

window.onerror = (message, source, lineno, colno) => {
  const err = { message, source, lineno, colno };
  fetch('/assets/php/error-log.php', {
    method: 'post',
    body: JSON.stringify(err)
  });
}

На сервере, если, например, сайт на PHP, можно написать такой простенький скрипт:

<?php
define('LOG_FILE', 'logs/' . date('Y-m-d') . '.log');
$json = file_get_contents('php://input');
$data = json_decode($json, true);
try {
  error_log('[' . date('d.m.Y h:i:s') . '] [' . $data['message'] . '] [' . $data['lineno'] . ', ' . $data['colno'] . '] [' . $data['source'] . '] [' . $_SERVER['HTTP_USER_AGENT'] . ']' . PHP_EOL, 3, LOG_FILE);
} catch(Exception $e) {
  $message = implode('; ', $data);
  error_log('[' . date('d.m.Y h:i:s') . '] [' . $message . '] [' . $_SERVER['HTTP_USER_AGENT'] . ']' . PHP_EOL, 3, LOG_FILE);
}

Его следует сохранить в файл /assets/php/error-log.php, а также в этом каталоге создать папку logs для сохранения в ней логов.

В результате когда на клиенте, то есть в браузере будет возникать JavaScript ошибки, они будут сохраняться на сервер в файл следующим образом:

Передача JavaScript ошибок, возникающих в браузере, на сервер

Понравилась статья? Поделить с друзьями:
  • Jeep ошибка p012c
  • Json data ошибка
  • Jeep grand cherokee wj ошибки климата
  • Jeep ошибка p0088
  • Jeep grand cherokee wj ошибка p0158