Чему мы можем научиться у Go

Я работаю над кодовой базой, где «законно» смешивать парадигмы обещаний и асинхронности/ожидания. Вот пример из недавнего обзора кода.

Здесь мы делаем вызов API, используя промис, но внутри обработчика catch мы используем async/await. См. строки 6 и 9.

Я нахожу это гротескным, но мои товарищи по команде думают, что это нормально.

Мой аргумент заключается в том, что асинхронный код достаточно сложен для понимания; смешение парадигм делает это еще сложнее. Их аргумент заключается в том, что приведенный выше фрагмент кода короче и легче читается, чем тот, который основан исключительно на промисах или исключительно на основе асинхронности/ожидания.

И знаешь, что? У них есть точка зрения. Рассмотрим тот же фрагмент кода, полностью написанный с помощью промисов.

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

Теперь рассмотрим тот же фрагмент кода, полностью написанный с помощью async/await.

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

Но что-то все равно кажется неправильным.

Это те самые исключения.

Во всех трех фрагментах кода возможность исключения создает беспорядок. И хотя использование async/await обычно считается лучшим подходом, этот блок try/catch является разрушительным.

Беспорядок в исключениях характерен не только для JavaScript. Другие языки боролись с этим, а некоторые предлагали альтернативы с разной степенью успеха. Я хочу сосредоточиться на одном из этих языков, в частности: Go.

Команда Go описывает уникальный подход языка к обработке ошибок в своем блоге 2011 года Обработка ошибок и Go.

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

Короче говоря, это означает, что такие функции, как api.updateFoo(), возвращают две вещи: результат и ошибку:

const [updatedFoo, err] = api.updateFoo(foo);

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

Вот как это выглядело бы, если бы мы использовали ошибки в стиле Go в JavaScript.

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

Что вы думаете?