Рубрики
Без рубрики

Пять уровней обработки ошибок в Python и JavaScript

Введение я выступил в Openslava 2020 несколько недель назад, в частности, вокруг уровней обработки ошибок, которые вы должны применить к кодированию. Тем не менее, я хотел, чтобы письменная статья обратилась к тем, кто не хочет смотреть видео. Ниже приведено 5 уровней обработки ошибок. Tagged с JavaScript, Python, ошибками, исключением.

Введение

Я Выступил в Openslava 2020 Несколько недель назад, в частности, вокруг уровней обработки ошибок, вы должны применить к кодированию. Тем не менее, я хотел, чтобы письменная статья обратилась к тем, кто не хочет смотреть видео.

Ниже приведено 5 уровней обработки ошибок. Я называю их «уровнями», потому что идея состоит в том, чтобы начать с самого низкого уровня, узнать, как это работает, а затем выравнивать до следующего. Идеально заключается в том, что вы используете обработку ошибок уровня 5, соответствие шаблонов, во всех типах кодирования, которые вы делаете независимо от языка. Если вы работаете на этом уровне, у вас будет более предсказуемый код. Есть и другие типы обработки ошибок, это всего лишь наиболее распространенные, которые я видел.

Дерево навыков обработки ошибок следующее:

🏎 lvl 1: Игнорировать EM, динамические языки имеют быструю итерацию ⚾ lvl 2: try/catch/throw 🏭 lvl 3: go/lua style, возвращаемые значения функции, переход назад ⛓ lvl 4: Стиль трубопровода, как Javascript обещание 🌯 lvl 5: Матч с шаблоном на возвращенных типах

Уровень 1: Игнорируйте их, без обработки ошибок

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

Например, здесь мы получаем доступ к свойству FirstName на словаре Python:

name = person["firstName"]

Это может либо работать, либо потерпеть неудачу с Keeerror, потому что первого имени не существует на человека. В Python и Javascript это обычное дело; Доступ к словарям и объектам с уверенностью и без обработки ошибок.

Вот более распространенный пример в JavaScript, где вы загружаете какой -то JSON с API:

const result =
  await fetch(url)
  .then( response => response.json() )

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

Существует множество веских причин, по которым вы можете намеренно захотеть сделать стиль 1 -го уровня «не заботы».

Игра с идеями и моделированием домена

Во -первых, когда вы играете с идеями, чтобы выучить свой домен. В программировании домен – это «проблема проблемная, которую вы пытаетесь решить». Это может быть так же мало, как преобразование температуры от Фаренгейта в Цельсия, так же большим, как строительство системы покупки и доставки онлайн -мебели, или вы даже не знаете, что прицелится. В этих ситуациях, независимо от того, задумались ли вы заранее, чтобы сделать архитектуру, или, возможно, вы просто думаете о более быстрых идеях кода, вы часто моделируете части домена различными способами.

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

Паттерн супервизора

Второй способ – это вы знаете, что работаете в системе, которая автоматически обрабатывает их для вас. Python и JavaScript имеют различные способы, используя Try/кроме | Попробуйте/поймать, чтобы справиться с синхронными ошибками и различными глобальными возможностями исключения. Однако, если вы работаете в архитектуре, которая автоматически ловит их, то если код достаточно прост, вам может быть все равно. Примеры включают AWS Lambda , AWS Шаг Функции , Контейнеры Docker, работающие на ECS или Эк . Или, может быть, вы кодируете Elixir/Erlang, который имеет философию ” Let It Crash “; Акка Имеет эту философию тоже. Все эти услуги и архитектуры поощряют ваш код сбой и Они справятся с этим , не вы. Это значительно упрощает вашу архитектуру и сколько кода вам нужно написать в зависимости от вашего языка.

Изучать новые вещи

Другая причина – учиться. Например, допустим, я хочу научиться делать сопоставление шаблонов в Python В И я не хочу Используйте библиотеку Анкет Я Прочтите этот пост в блоге и попробуйте примеры, которые автор издает. Ошибки могут помочь или нет; Дело в том, что моя цель состоит в том, чтобы выучить технику, я не заинтересован в том, чтобы поддерживать обработку кода или ошибок.

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

Уровень 2: попробуйте/кроме/поднять или попробовать/кроме/бросить

Уровень 2 – это когда вы вручную ловите синхронные ошибки, используя Try/кроме Python и Try/Catch в JavaScript. Я также смешаю различные асинхронные и глобальные исключения обработки исключений. Цель здесь состоит в том, чтобы поймать известные ошибки и либо зарегистрировать те, от которых вы не можете восстановиться, либо пройти другой путь кода для тех, которые вы можете, например, значения по умолчанию, либо повторение неудачного действия как 2 примера.

Насколько тщательно вы?

Python и JavaScript – это динамические языки, поэтому почти каждая часть языка может сбой. Например, в таких языках, как Java, есть ключевые слова, такие как Throwable, что заставляет компилятор сказать: «Эй, вы должны попробовать/поймать здесь». Поскольку у Java есть типы, несмотря на то, что он не натючен, есть еще много случаев, когда вам не нужно беспокоиться о авариях из -за этих типов. Это означает, что на самом деле нет никаких правил или хороших руководств о том, насколько тщательно вы должны получить обработку ошибок в вашем коде.

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

try:
  result = request(url)['Body'].json()
except Exception as e:
  print("failed to load JSON:", e)

Для тех, кто использует его повсюду, другие будут подвергать сомнению, каковы затраты на производительность и затраты на читаемость кода. В нашем первом имене доступа к словарю Python выше, если вы не используете линзы Тогда вы только обрабатываете, чтобы либо проверить наличие ключей:

if "firstName" in person:
  return person["firstName"]
return None

… Однако теперь у нас есть функции Python позже, ожидая, что строка получает Нет Вместо этого и бросая исключения. Подробнее об этом позже.

В JavaScript та же история с использованием Дополнительная цепочка Ищу вложенные свойства:

return person.address?.street

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

У программистов есть разные стили кодирования и убеждения, и поэтому, насколько они тщательны на этом уровне, действительно зависит от этого стиля и языка программирования.

Создавать ошибки или нет?

Уровень 2 включает в себя принятие этих ошибок в виде типов и механизмов, которые их используют. Для типов кода, где многие вещи могут пойти не так, как вы реализуете, что на уровне 2 создает различные ошибки для различных сбоев … возможно. Некоторые люди, использующие уровень 2, думают, что вы должны обрабатывать ошибки, но не создавать их. Другие говорят, что охватите то, что обеспечивает язык, а затем проверяет тип ошибки во время выполнения. Для Python и JavaScript это расширяет некоторые базовые классы ошибок.

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

class BadUrlError extends Error {}
class Timeout extends Error {}
class NetworkError extends Error {}
class BadStatus extends Error {}
class GoodStatus extends Error {}

Затем, когда вы делаете вызов, вы можете более четко знать, что пошло не так, и, возможно, отреагируйте на него, если вы сможете, например, журнал ошибки или повторная попытка:

try {
  const person = await loadPerson("/person/${id}")
} catch (error) {
  if(error instanceof BadUrlError) {
    console.log("Check '/person/${id}' as the URL because something went wrong there.")
  } else if(error instanceof Timeout || error instanceof NetworkError || error instanceof BadStatus) {
    retry( { func: loadPerson, retryAttempt: 2, maxAttempts: 3 })
  } else {
    console.log("Unknown error:", error)
    throw error
}

В вашем классе/функции обертки, вы специально будете бросить новый Badurlerror (...) Основываясь на интерпретации различных вещей, которые могут пойти не так с Fetch. Для любого, что вы упускаете, абонент, как предполагается, просто регистрирует и переигрывает его.

В Python этот стиль обработки исключений Java распространен, если автор либо исходит из этого языка, либо просто следует строгому объектно -ориентированному стилю программирования:

try:
  person = load_person(f'/person/{id}')
except BadUrlError:
  print(f'Check /person/{id} as the URL because something went wrong there.')
except Timeout:
except NetworkError:
except BadStatus:
  retry(func=load_person, retry_attempt=2, max_attempts=3)
except Exception as e:
  raise e

Уровень 3: Ошибки как возвращаемые значения

Lua и Go приблизились к обработке ошибок по -разному. Вместо того, чтобы рассматривать ошибки как отдельный механизм функций и классов, функция позволяет вам знать, сработал или нет. Это означает, что функции должны рассказать вам о 3 вещах: если это сработало или нет, если это сделало то, что является возвратным значением, и если это не так, какая ошибка. Минимум, вам нужно вернуть 2 вещи из функции вместо одной вещи.

И это то, что делают Луа и иди; Они позволяют возвращать несколько значений из функций.

В то время как Луа не обеспечивает соблюдение этого стиля кода, это нормальное соглашение в Голанге. Вот как будет обращаться с чтением файла:

f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}

Изменение нашего примера JavaScript HTTP, чтобы принять этот стиль, имея Loadperson вернуть Объект с ошибкой или человеком, но никогда не оба:

const { error, person } = await loadPerson("/person/${id}")
if(error) {
  return { error }
}

Python немного проще в том, что вы можете вернуть кортеж, а разрушение аргументов превращает их в переменные. load_person Функция вернет (Нет, Person_json) для успеха и (the_error, нет) за провал.

error, person = load_person(f'/person/{id}')
if error:
  return (error, None)

У этого есть некоторые плюсы и минусы. Давайте перейдем на первое место профессионала.

  1. Код становится очень процедурным, когда вы начинаете писать много функций вместе. Это очень легко следовать.
  2. Каждая функция может вернуть много возможных ошибок функций, которые она использует, и все они выходят одинаково; То же самое, как вы работаете с данными и ошибками.
  3. Нет необходимости в попытке/поймать/за исключением отдельной части языка; Вам больше не нужно беспокоиться о отдельном пути кода.
  4. Вы все еще можете отказаться от ошибок, таких как уровень 1, если вы хотите просто играть с кодом, или ошибки не имеют значения, но он не сломает код, как уровень 1, когда вы игнорируете их.

Минусы? Этот стиль, если вы обрабатываете все ошибки, может очень быстро стать словесной. Несмотря на использование краткого языка питона, он все еще может затянуться:

error, string = load_person_string(file_path)
if error:
  return (error, None)

error, people_list = parse_people_string(string)
if error:
  return (error, None)

error, names = filter_and_format_names(people_list)
if error:
  return (error, None)

return (None, names)

Последний пункт не состоит в том, чтобы все функции должны вернуть успех или сбои. Если вы знаете, что ваша функция не может потерпеть неудачу, имеет низкую вероятность, она это произойдет, или не выполняет никакого ввода -вывода, то вы можете просто вернуть свою ценность. Примеры включают в себя получение сегодняшней даты или какую ОС. Однако, учитывая Python и JavaScript динамичны, у вас нет гарантий во время выполнения. Даже используя Mypy или TypeScript, оба являются необработанными языками, поэтому, хотя это значительно увеличивает ваши шансы, вы все равно не можете быть уверены. Иногда лучше всего гибридный подход. Например, Boto3 , AWS Python SDK имеет чрезвычайно последовательное поведение почти со всеми методами «Если он работает, он возвращает данные; если это не так, это повышает исключение». Это означает, что вы можете очень хорошо принять уровень 3 с Python AWS SDK из -за этого последовательного поведения.

Уровень 4: трубопроводы

К счастью, эта проблема условности и повторения уже была решена на функциональных языках с использованием трубопроводов, также называемых Железнодорожное программирование Анкет Трубопроводы принимают эту концепцию функций, позволяющих вам знать, работали ли они или нет, и объединять их в одну функцию. Это очень похоже на то, как работают Луа и Голанг, за исключением меньшей сложности. Преимущества, помимо меньшего кода, в том, что вам нужно только определить обработку ошибок в 1 месте. Как и уровень 3, вы можете отказаться, если хотите, просто не определяя поймать Анкет

JavaScript asynchronous

В первую очередь мы достигнем JavaScript, так как это самый распространенный способ выполнить этот стиль обработки ошибок в трубопроводе.

fetch(someURL)
.then( response => response.json() )
.then( filterHumans )
.then( extractNames )
.then( names => names.map( name => name.toUpperCase() ) )
.catch( error => console.log("One of the numerous functions above broke:", error) )

Чтобы по -настоящему оценить вышеперечисленное, вы должны сравнить это с стилем Голанга, и вы узнаете, насколько проще читать и насколько меньше кода написать. Если вы просто играете с идеями, вы можете удалить поймать В конце концов, если вы не заботитесь о ошибках. Будь то Fetch Сбой с 5 возможными ошибками или response.json терпит неудачу, потому что это не проанализируемое JSON, или Возможно, Ответ испорчен, или любая из остальных функций … что угодно, они все сразу же остановится, когда у них будет ошибка и прыгнут прямо к части вылова. В противном случае результат одной функции автоматически помещается в следующую. Наконец, для JavaScript не имеет значения, является ли функция синхронной или асинхронной; это просто работает.

Питоны трубопроводы

Трубопроводы Python немного разные. На данный момент мы игнорируем Async/wait & Thread, а также предположим, что хорошая часть Python – это то, что синхронизация и асинхрон в основном чувствуют и выглядят одинаково в коде. Это вызывает Pro Python в том смысле, что вы можете использовать функции синхронного стиля, которые работают как для синхронизации, так и для асинхронного кода. Мы рассмотрим несколько.

Пидаш цепь

Давайте переписать пример JavaScript выше, используя Pydash’s цепь :

chain(request(some_url))
.thru(lambda res: res.json())
.filter( lambda person: person.type == 'human' )
.map( lambda human: human['name'] )
.map( lambda name: name.upper() )
.value()

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

Возвращает @Safe & Flow

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

Вместо функции уровня 3 в возвращении Python ошибка, данные , вместо этого он возвращает Результат . Думайте об этом, как базовый класс, который имеет 2 подкласса: Успех для данные и Неудача для ошибка . Хотя функция возвращает одно значение, это не главное; Настоящее веселье – теперь вы можете составить их вместе в одну функцию:

flow(
  safe_parse_json,
  bind(lambda person: person.type == 'human'),
  lambda human: get_or('no name', 'name', human),
  lambda name: name.upper()
)

Это даст вам Результат в конце; Либо это успешно, а Успех Тип, и ваши данные находятся внутри, или это Неудача и ошибка внутри. Как вы разверните, это зависит от вас. Вы можете позвонить развернуть И это даст вам ценность или бросит исключение. Или вы можете проверить, если это успешно; Много вариантов здесь. Возможно, вы работаете в контейнере Lambda или Docker и вам все равно, если у вас есть ошибки, так что просто используйте развернуть в конце. Или, может быть, вы используете уровень 3, потому что работаете с разработчиками GO, вынужденными использовать Python, так что преобразуйте его:

result = my_flow(...)
if is_successful(result) == False:
  return (result.failure(), None)
return (None, result.unwrap())

Де -факто трубы

Это такой распространенный шаблон, многие языки встроены в эту функциональность, и многие также абстрагируют, синхронно или нет. Примеры включают F# , РЕЗДПИСКА и Вяз Анкет Вот пример JavaScript, используя Вавилочный плагин и обратите внимание, что не имеет значения, асинхронно или синхронизация, как Обещание возвращаемое значение:

someURL
|> fetch
|> response => response.json()
|> filterHumans
|> extractNames
|> names => names.map( name => name.toUpperCase() )

Примечания по типам

Просто записка о типах здесь. В то время как JavaScript и Python не известны типами, в последнее время многие разработчики JavaScript приняли TypeScript и несколько разработчиков Python вышли за пределы Встроенные в типовые подсказки использовать mypy Анкет Для создания этих трубопроводов TypeScript 4.1 имеет Вариальные кортежи что может помочь, тогда как возврат делает все возможное, чтобы поддержать от 7 до 21 труб сильной печати. Это потому, что эти языки не были построены с учетом программирования железной дороги, если вам интересно, почему трение.

Уровень 5: Сопоставление рисунков

Последний уровень для этой статьи, сопоставление образцов похоже на более мощный оператор Switch за 3 способа. Во -первых, операторы переключения соответствуют значению, где большинство сопоставления шаблонов позволяет вам соответствовать многим типам значений, включая сильные типы. Во -вторых, операторы Switch не всегда должны возвращать значение, и при этом не соответствует сопоставлению шаблонов, но это чаще, что вы делаете. В -третьих, сопоставление рисунков имеет подразумеваемый улов, все это похоже на по умолчанию, который является сильным типом, аналогичным строгому режиму TypeScript для операторов коммутатора, гарантируя, что вы не можете пропустить случай Анкет

Сопоставление с шаблоном JavaScript

Вот основная функция в JavaScript, используя Фольклор Чтобы подтвердить имя.

const legitName = name => {
  if(typeof name !== 'string') {
    return Failure(["Name is not a String."])
  }

  if(name.length < 1 && name !== " ") {
    return Failure(["Name is not long enough, it needs to be at least 1 character and not an empty string."])
  }

  return Success(name)
}

Затем мы можем соответствовать рисунку в результате:

legitName("Jesse")
.matchWith({
  Failure: ({ value }) => console.log("Failed to validate:", value),
  Success: ({ value }) => console.log(value + " is a legit name.")
})

На момент написания этой статьи предложение JavaScript находится на этапе 1, но если вы приключны, есть Вавилочный плагин или Библиотека Sparkler Если Folktale не делает этого для вас.

Если бы вы написали это как оператор Switch, это может выглядеть как:

switch(legitName(value)) {
  case "not legit":
    console.log("Failed to validate:", getWhyInvalid(value))
    break

  case "legit":
    console.log(value + " is a legit name.")
    break

  default:
    console.log("Never get here.")
}

Несколько вещей, которые нужно отметить здесь. Во -первых, в соответствии с шаблонами вы обычно используете какой -то тип типа объединения. Принимая во внимание, что в словарках в Python может быть любое количество добавленных свойств или объектов в JavaScript одинаково, профсоюзы фиксируются. Наш Валидация Тип выше имеет только 2: Успех или Неудача Анкет Это означает, что нам нужно только матч с шаблоном 2. Если вы используете систему типа, то она знает, что есть только 2. Если вы сделаете 3, это будет кричать на вас. Если вы делаете просто Успех , вам будет кричать, что вам не хватает Неудача Анкет

Сравните это с оператором Switch, который понятия не имеет. Технически вам не нужно по умолчанию , но если только вы включаете, это союз, компилятор не знает, что Так что вы должны положить это туда, хотя это никогда не пойдет. Как глупо.

Паттерн Python, сопоставление через Pampy

Кроме того, оба примера не возвращают значение, но на самом деле это общая функциональность сопоставления шаблонов. Давайте реализуем наш вызов HTTP REST в качестве шаблона с использованием Python через БИБЛИОТЕКА PAMPY и мы вернем Python Union , в частности, Результат от возврата который либо сработал, и мы поместили данные в Успех или это не удалось, и мы поместили причину, почему в Отказ :

result = match(load_person(f'/person/{id}'),
  Json, lambda json_data: Success(json_data),
  BadUrl, lambda: Failure(f"Something is wrong with the url '/person/{id}'"),
  Timeout, lambda: retry(func=load_person, retry_attempt=2, max_attempts=3),
  NetworkError, lambda: retry(func=load_person, retry_attempt=2, max_attempts=3),
  BadStatus, lambda: retry(func=load_person, retry_attempt=2, max_attempts=3)
)

Для нашей первой попытки, если мы получим Json , круто, все сработало и наш результат Будут наши данные JSON, которые мы хотели.

Если у нас есть Badurl Тем не менее, у нас проблемы, потому что это означает, что что -то не так с нашим кодом в том, как мы написали URL, или, возможно, мы неправильно читаем его из переменная среды Мы думали, что там, но нет. Здесь мы ничего не можем сделать, кроме как исправить наш код и сделать его более устойчивым, возможно, предоставив значение по умолчанию с некоторой проверкой URL -адреса заранее.

Тем не менее, мы нарушаем сухой (не повторяйте себя) здесь немного по Тайм -аут , NetworkError и BadStatus Все делают то же самое, что пытались попытаться повторно. Поскольку вы обычно совпадаете с профсоюзами, вы заранее знаете, сколько возможных состояний (обычно; некоторые языки позволяют вам соответствовать рисунку на других вещах, которые имеют бесконечные пространства. Ради этой статьи мы просто сосредотачиваемся на ошибках). Таким образом, мы можем использовать этот поймать все, что является подчеркиванием (_). Давайте переписаем:

result = match(load_person(f'/person/{id}'),
  Json, lambda json_data: Success(json_data),
  BadUrl, lambda: Failure(f"Something is wrong with the url '/person/{id}'"),
  _, lambda: retry(func=load_person, retry_attempt=2, max_attempts=3)
)

Намного лучше. Также обратите внимание по сравнению с оператором Switch, вы знаете, что представляет _, и часто есть компилятор, чтобы помочь вам, тогда как коммутатор не всегда будет знать, что находится в дефолте. Наш пример выше содержит данные, сбой и, возможно, успех, если попытка будет успешной, иначе он в конечном итоге вернет ошибку после исчерпания его повторных попыток.

Если вы хотите что -то более питоническое, чем Pampy, вы можете попробовать Соответствие шаблона в Python с использованием обработки данных Анкет

Сопоставление рисунков – это больше, чем просто обработка ошибок

Одна тонкая вещь, которую нужно здесь, – это сопоставление образцов, часто является просто языковой особенностью на более функциональных языках. Таким образом, вы можете использовать его на каждом уровне обработки ошибок. Например, вот вышеуказанное в стиле 1 -го уровня “мне все равно, просто играть с идеями”:

result = match(load_person(f'/person/{id}'),
  Json, lambda json_data: Success(json_data),
  _, lambda: Success([]) # TODO: just empty Array for now, not sure why my parsing is failing, will fix later
)

Опять же, если вы работаете с разработчиками GO, вынужденными использовать Python, вы можете соответствовать шаблону до уровня 3:

result = match(load_person(f'/person/{id}'),
  Json, lambda json_data: (None, json_data),
  BadUrl, lambda: (Exception(f"Something is wrong with the url '/person/{id}'"), None),
  _, lambda: retry(func=load_person, retry_attempt=2, max_attempts=3)
)

Для 4 -го уровня многие трубопроводы просто предполагают, что все, что вы возвращаете из матча с шаблоном, возвращается в трубопровод. Например, наши люди проживание выше, если данные взяты из технического заполненного заполненного долга или базы данных с плохими данными, мы можем помочь компенсировать. Мы делаем это с помощью шаблона, сопоставляя на extract_names Чтобы мы просто предоставили по умолчанию по сравнению с нарушением всего трубопровода. Если чье -то имя было пустым, потому что вы не можете иметь нулевые значения в DynamoDB , это не должно останавливать все. Наконец, поскольку мы знаем все возможные результаты, мы совпадаем с шаблоном в поймать Чтобы функция никогда не удавалась, и вместо этого позвольте потребительскому шаблону совпадать с известными результатами. Для тех, кто не понимает обещаний и просто использует синтаксис Async/a a a a a a a a a a a a a a a a a a a ждать синтаксиса без Try/catch, это позволяет им делать это, не причиняя вреда кодовой базе. Во -первых, мы построим небольшой функциональный шаблон на возможность того, что мы получим человеческий объект без названия.

const getNameElseDefault = human =>
  getNameMaybe(human).matchWith({
    Nothing: () => "no name found",
    Just: ({ value }) => value
  })

Затем мы приведем ее в наш существующий трубопровод JavaScript ниже: (предположим, что мы изменили response.json () , чтобы добавить пользовательскую ошибку, как на уровне 2):

const getPeople = () =>
  Promise.resolve(someURL)
  .then( fetch )
  .then( response => response.json() )
  .then( filterHumans )
  .then(
    humans =>
      humans.map(getNameElseDefault)
  )
  .then( names => names.map( name => name.toUpperCase() ) )
  .then( uppercaseNames => Json(uppercaseNames) )
  .catch(
    error =>
      error => error.matchWith({
        FailedToParseJSON: parseError => Promise.resolve(parseError),
        BadUrl: badurlError => Promise.resolve(badurlError),
        _: otherError => Promise.resolve(otherError)
      })
  )

Теперь тот, кто потребляет эту функцию, может просто соответствовать шаблону по 2 значениям:

const result = await getPeople()
result.matchWith({
  Json: ({ uppercaseNames }) => console.log("Got our people names:", uppercaseNames),
  _ => error => console.log("Something broke:", error)
})

Плюсы и минусы сопоставления рисунков

Если вы не используете типы, преимущества аналогичны уровню 3 в том, что вы начинаете предполагать, что все функции никогда не выполняются, и вместо этого дайте вам знать, работало ли они, что они пытались или нет. Когда дела становятся все сложнее, чем только 2 возможных результата, таких как «успех» или «неудача», как это происходит в ответах HTTP, вы можете создать свой собственный и сопоставить их. Просто потому, что что -то имеет 5 возможных результатов, вы можете использовать Catch All _ Когда вам нужно объединить все ошибки в один или просто все равно. Нет необходимости выполнять ручную обработку ошибок, например, try/кроме/поймать.

Если вы используете типы, вы можете убедиться, что вы справились со всеми возможными совпадениями, поэтому вы никогда не пропустите тип возврата функции. Даже с типами вы все равно можете объединить их все в _ Если вы просто играете с идеями.

Тем не менее, многие языки не поддерживают эту функциональность изначально. Это медленно прикрепляется к Python и JavaScript Анкет Использование библиотек и методов, выше, может быть странным для тех, кто поступает из традиционного императивного или объектно -ориентированного Python/JavaScript. Уровень 3 – это достаточно тяжелая ласточка, чтобы сказать кому -то: «Вы знаете, как мы подняли/бросили исключения? Что, если у вас больше этого не было. ” Теперь вы говорите: «Все функции, которые могут потерпеть неудачу, мы возвращаем объект, и вам придется определить, как с ним справиться». Это очень много для многих разработчиков, особенно когда в большинстве традиционных программных литературы ссылается: «Да, предполагается, что вы просто используете Try/Catch».

Наконец, без типов, вы обычно можете сделать OK, используя Может и Результат поскольку со временем относительно легко запомнить, их 2 подтипа, такие как справедливо/ничего и успех/неудача. Но когда вы создаете пользовательские, или начинаете гнездовать их в составленные функции и понятия не имеете, что выходит, это может быть сложно. Те, кто уже чувствует себя комфортно с динамическими языками, обычно хороши с печати вывод, чтобы узнать, какие это типы, против использования типизированного языка, чтобы компилятор помог вам.

Выводы

Я объяснил 5 уровней обработки ошибок, особенно для динамических языков:

  1. Вы игнорируете их
  2. Вы справляетесь с ними, используя Try/кроме/поднимаете/бросаете в разные степени
  3. Вы принимаете метод возврата нескольких значений Lua/Golang
  4. Вы создаете трубопроводы и обрабатываете ошибку в 1 место против многих подобных уровней 3
  5. Вы соответствуете результатам, что функция может вернуть, например, успех или сбой, или более нюансированные результаты, такие как HTTP, с использованием функций вместо сопоставления исключений, как на уровне 2

Хотя важно знать каждый уровень, и каждый из них имеет использование, вы захотите использовать уровень 4 и 5 для производственного кода. Вы должны зарезервировать право игнорировать ошибки и жить на уровне 1, когда вы учитесь решить свою проблему. Однако, когда вы будете готовы начать кодировать проект на реальное, стремиться к уровню 4 и 5. Они обеспечивают наименее неожиданные исключения во время выполнения и меньше накладных расходов в модульном тестировании функциональности.

Для динамических языков большая часть ответственности за вас запомнит типы и формы словарей/объектов. Уровень 1 и 2 трудны, потому что иногда вы просто получаете Исключение или Ошибка и другие типы типов ошибок задокументированы. Они полезны для регистрации, так как многие API и SDK построены таким образом, чтобы помочь вам выяснить, что сломалось в их абстракциях. Однако со временем вы найдете, что, помимо журнала, вы всегда окажетесь в «Она либо работала, либо не сделала этого», и вы начнете отказаться от стеков обработки исключений в журнале. Вы никогда не достигнете консенсуса с вашей командой или самим собой о том, сколько попыток/кроме как достаточно. Вы изо всех сил пытаетесь увидеть возврат инвестиций в создание пользовательских классов исключений.

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

Существуют различные варианты трубопровода для Python, и даже JavaScript имеет альтернативы Обещание как Rxjs . Вы обнаружите, однако, что концепция класса ошибок не очень полезна, если вы не можете легко сравнить ее с другими, и поэтому сопоставление шаблонов 5 -го уровня намного лучше с рабочими процессами трубопровода, как в уменьшении необходимого кода шаблона В проверке ошибок уровня 3 и возможности просто вводить в любое время в желаемом трубопроводе. Большая документация по сопоставлению рисунков будет охватывать полосу вещей, на которых вы можете сопоставить, например, простые числа и списки, но для обработки ошибок это предполагает какой -то своего рода DataClass или тип. В то время как трубопроводы, такие как JavaScript, обещают выплюнуть данные или повышать исключение, лучше, если вы относитесь к ним, как функции уровня 3, которые возвращают значения успеха/сбоя, и оттуда переходите.

Оригинал: “https://dev.to/jesterxl/five-levels-of-error-handling-in-both-python-and-javascript-13ok”