From ea8615afeee05a35af41fdfbaa699e00c9edcbea Mon Sep 17 00:00:00 2001 From: Serhii Shramko Date: Tue, 17 Sep 2024 14:25:14 -0400 Subject: [PATCH] docs: update UA readme --- README.ukrainian.md | 104 ++++--- .../eslint_prettier.ukrainian.md | 25 ++ ...lfast.ukraine.md => failfast.ukrainian.md} | 0 .../returningpromises.ukrainian.md | 283 ++++++++++++++++++ 4 files changed, 358 insertions(+), 54 deletions(-) create mode 100644 sections/codestylepractices/eslint_prettier.ukrainian.md rename sections/errorhandling/{failfast.ukraine.md => failfast.ukrainian.md} (100%) create mode 100644 sections/errorhandling/returningpromises.ukrainian.md diff --git a/README.ukrainian.md b/README.ukrainian.md index c1ed61d52..e3bf9520a 100644 --- a/README.ukrainian.md +++ b/README.ukrainian.md @@ -380,21 +380,17 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin **В іншому випадку:** Уявіть таку ситуацію – ваша функція очікує числовий аргумент "Знижка", який викликаюча сторона забуває передати, потім ваш код перевіряє, чи Знижка!=0 (розмір дозволеної знижки більший ніж нуль), тоді він дозволить користувачу отримати знижку. О боже, яка неприємна помилка. Бачите її? -🔗 [**Читати більше: швидке виявлення помилок**](./sections/errorhandling/failfast.md) +🔗 [**Читати більше: швидке виявлення помилок**](./sections/errorhandling/failfast.ukrainian.md)

-## ![✔] 2.12 Always await promises before returning to avoid a partial stacktrace +## ![✔] 2.12 Завжди очікуйте проміси перед поверненням, щоб уникнути часткового стеку викликів -**TL;DR:** Always do `return await` when returning a promise to benefit full error stacktrace. If a -function returns a promise, that function must be declared as `async` function and explicitly -`await` the promise before returning it +**Коротко:** Завжди використовуйте `return await` при поверненні промісу, щоб отримати повний стек помилок. Якщо функція повертає проміс, ця функція повинна бути оголошена як `async` функція і явно використовувати `await` для промісу перед його поверненням -**Otherwise:** The function that returns a promise without awaiting won't appear in the stacktrace. -Such missing frames would probably complicate the understanding of the flow that leads to the error, -especially if the cause of the abnormal behavior is inside of the missing function +**В іншому випадку:** Функція, яка повертає проміс без очікування, не з'явиться у стеку викликів. Такі відсутні кадри, ймовірно, ускладнять розуміння потоку, що призвів до помилки, особливо якщо причина аномальної поведінки знаходиться всередині відсутньої функції -🔗 [**Read More: returning promises**](./sections/errorhandling/returningpromises.md) +🔗 [**Читати більше: повернення промісу**](./sections/errorhandling/returningpromises.ukrainian.md)


@@ -402,73 +398,73 @@ especially if the cause of the abnormal behavior is inside of the missing functi # `3. Code Style Practices` -## ![✔] 3.1 Use ESLint +## ![✔] 3.1 Використовуйте ESLint -**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) and [beautify](https://www.npmjs.com/package/js-beautify) are more powerful in formatting the fix and work in conjunction with ESLint +**Коротко:** [ESLint](https://eslint.org) є фактичним стандартом для перевірки можливих помилок у коді та виправлення стилю коду. Він допомагає не лише виявити дрібні проблеми зі структурою коду, такі як зайві пробіли, але й серйозні антипатерни коду, наприклад, коли розробники кидають помилки без класифікації. Хоча ESLint може автоматично виправляти стиль коду, інші інструменти, такі як [prettier](https://www.npmjs.com/package/prettier) і [beautify](https://www.npmjs.com/package/js-beautify), більш потужні у форматуванні виправлень і працюють разом з ESLint. -**Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style +**В іншому випадку:** Розробники зосереджуватимуться на нудних питаннях щодо пробілів та ширини рядків, а час може бути витрачено на надмірне обдумування стилю коду проєкту. -🔗 [**Read More: Using ESLint and Prettier**](./sections/codestylepractices/eslint_prettier.md) +🔗 [**Детальніше: Використання ESLint та Prettier**](./sections/codestylepractices/eslint_prettier.ukrainian.md)

-## ![✔] 3.2 Node.js specific plugins +## ![✔] 3.2 Специфічні плагіни для Node.js -**TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security) +**Коротко:** Окрім стандартних правил ESLint, що охоплюють ванільний JavaScript, додайте специфічні плагіни для Node.js, такі як [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) і [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security). -**Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early +**Інакше:** Багато проблемних шаблонів коду Node.js можуть залишитися непоміченими. Наприклад, розробники можуть використовувати require(variableAsPath) для підключення файлів з передачею змінної як шляху, що дозволяє зловмисникам виконувати будь-який JS-скрипт. Лінтери Node.js можуть виявляти такі шаблони та попереджати про них заздалегідь.

-## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line +## ![✔] 3.3 Відкривайте фігурні дужки коду на тому ж рядку -**TL;DR:** The opening curly braces of a code block should be on the same line as the opening statement +**Коротко:** Відкриваючі фігурні дужки блоку коду повинні бути на тому ж рядку, що й оператор відкриття. -### Code Example +### Приклад коду ```javascript -// Do +// Правильно function someFunction() { - // code block + // блок коду } -// Avoid +// Уникайте function someFunction() { - // code block + // блок коду } ``` -**Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: +**Інакше:** Відхилення від цієї практики може призвести до несподіваних результатів, як показано в обговоренні на StackOverflow нижче: -🔗 [**Read more:** "Why do results vary based on curly brace placement?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) +🔗 [**Читати більше:** "Чому результати варіюються залежно від розміщення фігурних дужок?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement)

-## ![✔] 3.4 Separate your statements properly +## ![✔] 3.4 Правильно розділяйте свої вирази -No matter if you use semicolons or not to separate your statements, knowing the common pitfalls of improper linebreaks or automatic semicolon insertion, will help you to eliminate regular syntax errors. +Незалежно від того, використовуєте ви крапки з комами для розділення виразів чи ні, знання поширених помилок, пов'язаних з неправильними розривами рядків або автоматичною вставкою крапок з комами, допоможе уникнути регулярних синтаксичних помилок. -**TL;DR:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. +**Коротко:** Використовуйте ESLint для підвищення обізнаності про проблеми з розділенням виразів. [Prettier](https://prettier.io/) або [Standardjs](https://standardjs.com/) можуть автоматично вирішити ці проблеми. -**Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. +**Інакше:** Як було показано в попередньому розділі, інтерпретатор JavaScript автоматично додає крапку з комою в кінці виразу, якщо її немає, або вважає, що вираз не завершено там, де це потрібно, що може призвести до небажаних результатів. Ви можете використовувати присвоєння і уникати безпосередньо викликаних функціональних виразів (IIFE), щоб уникнути більшості несподіваних помилок. -### Code example +### Приклад коду ```javascript -// Do +// Правильно function doThing() { // ... } doThing() -// Do +// Правильно const items = [1, 2, 3] items.forEach(console.log) -// Avoid — throws exception +// Неправильно — виникає виняток const m = new Map() const a = [1,2,3] [...m.values()].forEach(console.log) @@ -476,61 +472,61 @@ const a = [1,2,3] > ^^^ > SyntaxError: Unexpected token ... -// Avoid — throws exception -const count = 2 // it tries to run 2(), but 2 is not a function +// Неправильно — виникає виняток +const count = 2 // інтерпретатор намагається виконати 2(), але 2 не є функцією (function doSomething() { - // do something amazing + // зробити щось чудове }()) -// put a semicolon before the immediate invoked function, after the const definition, save the return value of the anonymous function to a variable or avoid IIFEs altogether +// вставте крапку з комою перед безпосередньо викликаним виразом, після визначення const, збережіть результат анонімної функції у змінну або уникайте IIFE взагалі ``` -🔗 [**Read more:** "Semi ESLint rule"](https://eslint.org/docs/rules/semi) -🔗 [**Read more:** "No unexpected multiline ESLint rule"](https://eslint.org/docs/rules/no-unexpected-multiline) +🔗 [**Читати більше:** "Правило ESLint для крапок з комами"](https://eslint.org/docs/rules/semi) +🔗 [**Читати більше:** "Правило ESLint для несподіваних розривів рядків"](https://eslint.org/docs/rules/no-unexpected-multiline)

-## ![✔] 3.5 Name your functions +## ![✔] 3.5 Давайте імена своїм функціям -**TL;DR:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot +**Коротко:** Давайте імена всім функціям, включно із замиканнями та зворотними викликами. Уникайте анонімних функцій. Це особливо корисно при профілюванні Node.js додатків. Присвоєння імен функціям дозволить вам легко розуміти, що ви переглядаєте під час аналізу знімку пам'яті. -**Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions +**Інакше:** Виправлення проблем на продакшені за допомогою core dump (знімка пам'яті) може стати складним завданням, оскільки ви можете помітити значне споживання пам'яті анонімними функціями.

-## ![✔] 3.6 Use naming conventions for variables, constants, functions and classes +## ![✔] 3.6 Використовуйте угоди про іменування для змінних, констант, функцій і класів -**TL;DR:** Use **_lowerCamelCase_** when naming constants, variables and functions, **_UpperCamelCase_** (capital first letter as well) when naming classes and **_UPPER_SNAKE_CASE_** when naming global or static variables. This will help you to easily distinguish between plain variables, functions, classes that require instantiation and variables declared at global module scope. Use descriptive names, but try to keep them short +**Коротко:** Використовуйте **_lowerCamelCase_** для називання констант, змінних і функцій, **_UpperCamelCase_** (перша велика літера) для називання класів і **_UPPER_SNAKE_CASE_** для називання глобальних або статичних змінних. Це допоможе легко розрізняти звичайні змінні, функції, класи, які потребують інстанціювання, та змінні, оголошені на рівні глобального модуля. Використовуйте описові імена, але намагайтеся робити їх короткими. -**Otherwise:** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase +**Інакше:** JavaScript — єдина мова у світі, яка дозволяє викликати конструктор ("Клас") напряму без попередньої інстанціювання. Як наслідок, класи та функції-конструктори відрізняються тим, що починаються з UpperCamelCase. -### 3.6 Code Example +### 3.6 Приклад коду ```javascript -// for global variables names we use the const/let keyword and UPPER_SNAKE_CASE +// для глобальних змінних використовуємо ключові слова const/let і UPPER_SNAKE_CASE let MUTABLE_GLOBAL = "mutable value" const GLOBAL_CONSTANT = "immutable value"; const CONFIG = { key: "value", }; -// examples of UPPER_SNAKE_CASE convention in nodejs/javascript ecosystem -// in javascript Math.PI module +// приклади угоди UPPER_SNAKE_CASE у екосистемі nodejs/javascript +// у модулі JavaScript Math.PI const PI = 3.141592653589793; // https://github.com/nodejs/node/blob/b9f36062d7b5c5039498e98d2f2c180dca2a7065/lib/internal/http2/core.js#L303 -// in nodejs http2 module +// у модулі nodejs http2 const HTTP_STATUS_OK = 200; const HTTP_STATUS_CREATED = 201; -// for class name we use UpperCamelCase +// для назв класів використовуємо UpperCamelCase class SomeClassExample { - // for static class properties we use UPPER_SNAKE_CASE + // для статичних властивостей класів використовуємо UPPER_SNAKE_CASE static STATIC_PROPERTY = "value"; } -// for functions names we use lowerCamelCase +// для назв функцій використовуємо lowerCamelCase function doSomething() { - // for scoped variable names we use the const/let keyword and lowerCamelCase + // для локальних змінних використовуємо ключові слова const/let і lowerCamelCase const someConstExample = "immutable value"; let someMutableExample = "mutable value"; } diff --git a/sections/codestylepractices/eslint_prettier.ukrainian.md b/sections/codestylepractices/eslint_prettier.ukrainian.md new file mode 100644 index 000000000..dddd08ed5 --- /dev/null +++ b/sections/codestylepractices/eslint_prettier.ukrainian.md @@ -0,0 +1,25 @@ +# Використання ESLint та Prettier + +### Порівняння ESLint та Prettier + +Якщо ви відформатуєте цей код за допомогою ESLint, він лише видасть попередження про те, що рядок занадто широкий (залежить від налаштувань `max-len`). Prettier автоматично відформатує його для вас. + +```javascript +foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne(), noWayYouGottaBeKiddingMe()); +``` + +```javascript +foo( + reallyLongArg(), + omgSoManyParameters(), + IShouldRefactorThis(), + isThereSeriouslyAnotherOne(), + noWayYouGottaBeKiddingMe() +); +``` + +Джерело: [https://github.com/prettier/prettier-eslint/issues/101](https://github.com/prettier/prettier-eslint/issues/101) + +### Інтеграція ESLint та Prettier + +ESLint та Prettier перетинаються у функції форматування коду, але їх можна легко поєднати за допомогою інших пакетів, таких як [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) та [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier). Для отримання додаткової інформації про їхні відмінності, ви можете переглянути посилання [тут](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint). diff --git a/sections/errorhandling/failfast.ukraine.md b/sections/errorhandling/failfast.ukrainian.md similarity index 100% rename from sections/errorhandling/failfast.ukraine.md rename to sections/errorhandling/failfast.ukrainian.md diff --git a/sections/errorhandling/returningpromises.ukrainian.md b/sections/errorhandling/returningpromises.ukrainian.md new file mode 100644 index 000000000..f3302436d --- /dev/null +++ b/sections/errorhandling/returningpromises.ukrainian.md @@ -0,0 +1,283 @@ +# Повернення промісів + +
+ +### Пояснення за один абзац + +Коли виникає помилка, незалежно від того, чи це синхронний чи асинхронний потік, важливо мати повний стек викликів для потоку помилки. Дивно, але якщо асинхронна функція повертає проміс (наприклад, викликає іншу асинхронну функцію) без очікування, то у разі виникнення помилки функція-викликач не з'явиться у стеку викликів. Це залишить людину, яка діагностує помилку, з неповною інформацією - тим більше, якщо причина помилки лежить у межах цієї функції-викликача. Існує функція v8 під назвою "zero-cost async stacktraces", яка дозволяє стеку викликів не обриватися на останньому `await`. Але через нетривіальні деталі реалізації, вона не працюватиме, якщо значення, що повертається функцією (синхронною чи асинхронною), є промісом. Отже, щоб уникнути прогалин у стеках викликів, коли повернуті проміси будуть відхилені, ми завжди повинні явно вирішувати проміси за допомогою `await` перед їх поверненням з функцій. + +
+ +### Приклад коду Анти-Шаблон: Виклик асинхронної функції без очікування + +
Javascript +

+ +```javascript +async function throwAsync(msg) { + await null // потрібно очікувати хоча б щось, щоб бути дійсно асинхронним (див. примітку #2) + throw Error(msg) +} + +async function returnWithoutAwait () { + return throwAsync('відсутній returnWithoutAwait у стеку викликів') +} + +// 👎 НЕ матиме returnWithoutAwait у стеку викликів +returnWithoutAwait().catch(console.log) +``` + +виведе + +``` +Error: відсутній returnWithoutAwait у стеку викликів + at throwAsync ([...]) +``` +

+
+ +### Приклад коду: Виклик та очікування належним чином + +
Javascript +

+ +```javascript +async function throwAsync(msg) { + await null // потрібно очікувати хоча б щось, щоб бути дійсно асинхронним (див. примітку #2) + throw Error(msg) +} + +async function returnWithAwait() { + return await throwAsync('з усіма присутніми кадрами') +} + +// 👍 матиме returnWithAwait у стеку викликів +returnWithAwait().catch(console.log) +``` + +виведе + +``` +Error: з усіма присутніми кадрами + at throwAsync ([...]) + at async returnWithAwait ([...]) +``` + +

+
+ +
+ +### Приклад коду Анти-Шаблон: Повернення промісу без позначення функції як асинхронної + +
Javascript +

+ +```javascript +async function throwAsync () { + await null // потрібно очікувати хоча б щось, щоб бути дійсно асинхронним (див. примітку #2) + throw Error('відсутній syncFn у стеку викликів') +} + +function syncFn () { + return throwAsync() +} + +async function asyncFn () { + return await syncFn() +} + +// 👎 syncFn буде відсутня у стеку викликів, оскільки вона повертає проміс, будучи синхронною +asyncFn().catch(console.log) +``` + +виведе + +``` +Error: відсутній syncFn у стеку викликів + at throwAsync ([...]) + at async asyncFn ([...]) +``` + +

+
+ +### Приклад коду: Позначення функції, що повертає проміс, як асинхронної + +
Javascript +

+ +```javascript +async function throwAsync () { + await null // потрібно очікувати хоча б щось, щоб бути дійсно асинхронним (див. примітку #2) + throw Error('з усіма присутніми кадрами') +} + +async function changedFromSyncToAsyncFn () { + return await throwAsync() +} + +async function asyncFn () { + return await changedFromSyncToAsyncFn() +} + +// 👍 тепер changedFromSyncToAsyncFn буде присутня у стеку викликів +asyncFn().catch(console.log) +``` + +виведе + +``` +Error: з усіма присутніми кадрами + at throwAsync ([...]) + at changedFromSyncToAsyncFn ([...]) + at async asyncFn ([...]) +``` + +

+
+ +
+ +### Приклад коду Анти-шаблон #3: безпосереднє використання асинхронного зворотного виклику там, де очікується синхронний + +
Javascript +

+ +```javascript +async function getUser (id) { + await null + if (!id) throw Error('у стеку викликів відсутнє місце, де було викликано getUser') + return {id} +} + +const userIds = [1, 2, 0, 3] + +// 👎 стек викликів включатиме функцію getUser, але не дасть жодної підказки, де її було викликано +Promise.all(userIds.map(getUser)).catch(console.log) +``` + +виведе + +``` +Error: у стеку викликів відсутнє місце, де було викликано getUser + at getUser ([...]) + at async Promise.all (index 2) +``` + +*Примітка*: може здатися, що `Promise.all (index 2)` може допомогти зрозуміти, де було викликано `getUser`, +але через [зовсім іншу помилку в v8](https://bugs.chromium.org/p/v8/issues/detail?id=9023), `(index 2)` є +рядком з внутрішніх механізмів v8 + +

+
+ +### Приклад коду: обгортання асинхронного зворотного виклику у фіктивну асинхронну функцію перед передачею її як синхронного зворотного виклику + +
Javascript +

+ +*Примітка 1*: якщо ви контролюєте код функції, яка викликатиме зворотний виклик - просто змініть цю функцію на +асинхронну і додайте `await` перед викликом зворотного виклику. Нижче я припускаю, що ви не відповідаєте за код, який викликає +зворотний виклик (або його зміна неприйнятна, наприклад, через зворотню сумісність) + +*Примітка 2*: досить часто використання асинхронного зворотного виклику в місцях, де очікується синхронний, взагалі не працюватиме. Це не про +те, як виправити код, який не працює - це про те, як виправити стек викликів у випадку, якщо код вже працює як +очікувалося + +```javascript +async function getUser (id) { + await null + if (!id) throw Error('з усіма присутніми кадрами') + return {id} +} + +const userIds = [1, 2, 0, 3] + +// 👍 тепер рядок нижче є у стеку викликів +Promise.all(userIds.map(async id => await getUser(id))).catch(console.log) +``` + +виведе + +``` +Error: з усіма присутніми кадрами + at getUser ([...]) + at async ([...]) + at async Promise.all (index 2) +``` + +де завдяки явному `await` в `map`, кінець рядка `at async ([...])` вказуватиме на точне місце, де +було викликано `getUser` + +*Примітка*: якщо асинхронна функція, яка обгортає `getUser`, пропустить `await` перед поверненням (анти-шаблон #1 + анти-шаблон #3) +тоді у стеку викликів залишиться лише один кадр: + +```javascript +[...] + +// 👎 анти-шаблон 1 + анти-шаблон 3 - у стеку викликів залишився лише один кадр +Promise.all(userIds.map(async id => getUser(id))).catch(console.log) +``` + +виведе + +``` +Error: [...] + at getUser ([...]) +``` + +

+
+ +
+ +## Розширене пояснення + +Механізми стеків викликів синхронних функцій та асинхронних функцій у реалізації v8 досить різні: +синхронний стек викликів базується на **стеку**, наданому операційною системою, на якій працює Node.js (як у більшості мов програмування). +Коли виконується асинхронна функція, **стек** операційної системи виштовхує її, як тільки +функція доходить до свого першого `await`. Тому асинхронний стек викликів є сумішшю **стеку** операційної системи та відхиленого +**ланцюжка вирішення промісів**. Реалізація асинхронних стеків викликів з нульовою вартістю розширює **ланцюжок вирішення промісів** +тільки тоді, коли проміс очікується за допомогою `await` [¹](#1). Оскільки тільки `async` функції можуть використовувати `await`, +синхронна функція завжди буде відсутня в асинхронному стеку викликів, якщо будь-яка асинхронна операція була виконана після того, +як функція була викликана [²](#2) + +### Компроміс + +Кожен `await` створює нову мікрозадачу в циклі подій, тому додавання більшої кількості `await` до коду +призведе до деякого зниження продуктивності. Тим не менш, зниження продуктивності, спричинене мережею або +базою даних, є [надзвичайно більшим](https://colin-scott.github.io/personal_website/research/interactive_latency.html), +тому додаткова затримка від `await` не є чимось, що слід враховувати під час розробки веб-серверів або CLI, +якщо тільки це не дуже гарячий код для запиту або команди. Тому видалення `await` у +`return await` повинно бути одним з останніх місць для пошуку помітного підвищення продуктивності і +точно ніколи не повинно робитися заздалегідь. + + +### Чому return await вважався анти-шаблоном у минулому + +Існує ряд [чудових статей](https://jakearchibald.com/2017/await-vs-return-vs-return-await/), які пояснюють, +чому `return await` ніколи не слід використовувати поза блоком `try`, і навіть +[правило ESLint](https://eslint.org/docs/rules/no-return-await), яке забороняє його. Причина цього полягає в тому, що +з моменту, коли async/await стали доступними з транспіляторами в Node.js 0.10 (і отримали нативну підтримку в Node.js 7.6) і до +того, як "асинхронні стеки викликів з нульовою вартістю" були введені в Node.js 10 і розблоковані в Node.js 12, `return await` був абсолютно +еквівалентним `return` для будь-якого коду поза блоком `try`. Це може все ще бути так для деяких інших ES-двигунів. Ось чому +вирішення промісів перед їх поверненням є найкращою практикою для Node.js, а не для EcmaScript в цілому. + +### Примітки: + +1. Ще одна причина, чому асинхронний стек викликів має таку хитру реалізацію, - це обмеження, що стек викликів + завжди повинен будуватися синхронно, на тому ж тику циклу подій [¹](#1) +2. Без `await` в `throwAsync` код буде виконуватися в тій же фазі циклу подій. Це + вироджений випадок, коли **стек** ОС не буде порожнім і стек викликів буде повним навіть без явного + очікування результату функції. Зазвичай використання промісів включає деякі асинхронні операції, і тому частини + стеку викликів будуть втрачені +3. Асинхронні стеки викликів з нульовою вартістю все ще не працюватимуть для складних використань промісів, наприклад, коли один проміс + очікується багато разів у різних місцях + +### Посилання: +1. [Блог-пост про асинхронні стеки викликів з нульовою вартістю в v8](https://v8.dev/blog/fast-async) +
+ +2. [Документ про асинхронні стеки викликів з нульовою вартістю із згаданими тут деталями реалізації]( +https://docs.google.com/document/d/13Sy_kBIJGP0XT34V1CV3nkWya4T