Node.js Recipes
3.23K subscribers
174 photos
7 videos
1 file
622 links
По буднях нотатки по #Nodejs розробці, по вихідним огляди конференцій та доповідей (с) @galkin_nikita
Download Telegram
Как и зачем использовать AsyncLocalStorage?
#nodejs_api

С 16.4 версии Node.js AsyncLocalStorage перестал быть экспериментальным, а значит его можно смело использовать в продакшене. Данный класс позволяет сохранять контекст между шагами асинхронного флоу. Он работает и с callback, и с promise chain.

Для получения текущего значения используется asyncLocalStorage.getStore()

Для установки значения есть два способа:
asyncLocalStorage.run(store, callback[, ...args]) – устанавливает значение внутри callback. Для использования скорее всего потребуется внести изменения на верхнем уровне кода.
asyncLocalStorage.enterWith(store) – устанавливает значение до окончания текущего синхронного контекста и всех порождающих асинхронных операций. Проще в использование, но возможность его вызвать несколько раз создает сложности в поддержки. Мутировать стейт это плохо. Поэтому документация рекомендует использовать run.

Возможное использование данного апи – улучшение логгирования. В стейте храниться traceId, который пишется в лог. Так вы легко можете различить логи от нескольких запросов, которые параллельно обрабатываются #nodejs.

Статья с примерами использования из комментариев.
Пример для Nest.js.
👍1
Как использовать Node.js REPL?
#nodejs_api

REPL расшифровывается как read–eval–print loop. Для запуска #nodejs в таком режиме достаточно выполнить node без указания файла.

Работа с REPL очень похожа на работу с Console Panel в Google Chrome:
- пишешь JS код, его результат выводиться в консоль
- Cmd + K on MacOS / Ctrl + L on Windows – очистят консоль

Но есть и различия:
- Переменная _ хранит значение последней команды
- Есть команды начинающиеся с точки. Полный перечень .help
- Чтобы выйти из REPL используйте горячую клавишу Ctrl+D или Ctrl+C дважды

При желание можно написать свой REPL. Например для работы с API или базой данных, как это сделано для MongoDB. Подробней в документации.
В чем различие таймеров в Node.js и browser?
#nodejs_api

Сегодня обсудим setTimeout и setInterval. На обоих платформах данные функции доступны глобально и предназначены для запуска отложенного кода.

Первым аргументом должен быть TimerHandler. В #nodejs это может быть только функция, но в браузере это может быть еще и строка. К ней будет применен eval. Что безусловно является плохой практикой, так как может привести к проблемам с безопасностью. Используйте eslint правило no-implied-eval, чтобы следить за кодом.

Вторым аргументом является количество миллисекунд, а дальнейшие параметры будут переданы в качестве аргументов внутрь TimerHandler. Тут различий нет. Различие есть в возвращаемом значение. В браузере это будет число, а в Node.js объект типа NodeJS.Timeout.

NodeJS.Timeout имеет такие методы публичные методы:
- unref – отметить таймаут как не активный, т.е. он не будет удерживать EventLoop. Аналогичные вещи есть у сокетов, воркеров и т.д.
- ref – отменяет unref
- hasRef – дает текущее значение активности таймера.
- refresh – перезапускает таймер.
- close – аналогичен вызову clearInterval на таймере.

Чтобы отменить выполнение таймера используется clearInterval/clearTimeout. Они полностью заменяемы и в браузере, и в Node.js – по сути являются алиасами. Node.js в качестве аргумента принимает как число, так и NodeJS.Timeout объект.

На этом пора остановиться, хотя в рецепт не попали setImmediate, не публичные апи, особенности поведения в браузере в зависимости от активности таба, связь async_hooks и таймеров, event loop delay и прочее.
Как обнаружить синхронные операции в Node.js коде?
#nodejs_api

Каждый #nodejs разработчик знает, что блокировать Event Loop-а плохо. Блокировка может быть по двум причинам:
– cpu intensive операции, т.е. с большим количеством вычислений. Примеры: трансформация JSON объекта на 2 мегабайта, обход большого массива, подсчеты хешей.
– синхронные операции, т.е. операции которые до своего завершения блокируют Event Loop, читай дальнейшее выполнение JavaScript. Пример – все синхронные файловые операции.

Ваш код может использовать синхронные операции неявно. Кто его знает, что там в node_modules. Именно о том, как быстро найти такие места этот рецепт.

У Node.js есть специальный флаг --trace-sync-io. При его использование в консоль будут выводить trace для первого вызова любого синхронного вызова. Запускаем с этим флагом или c переменной окружения NODE_OPTIONS=--trace-sync-io и анализируем результаты. На своем проекте я так обнаружил, что пора выкинуть module-alias и переконфигурировать pino.

Рецепты по теме:
- Как запускать Node.js с доп. аргументами?
Обзор Node.js v16: Что такое corepack и как он работает?
#nodejs_api

В Node.js v16.9 добавили corepack. Данный инструмент является экспериментальным и упрощать работу с альтернативными менеджеров зависимостей. При обращение к yarn или pnpm (поддерживаемые менеджеры) будет происходит вызов установленных corepack-ом так называемые глобальных binary proxies. Данные прокси смотрят поле packageManager в package.json и вызывает соответствующую версию yarn/pnpm. При необходимости происходит установка отсутствующего менеджера. Если package.json нет, то используется LTS версия менеджера. Поддержки npm нет, так как npm устанавливается глобально вместе с Node.js. Инструмент улучшает Repeatability и решает проблему отсутствия аналогов nvm для yarn/pnp.

Мое личное отношение к альтернативным менеджерам – они расслаивают экосистему и комьюнити. Я стараюсь избегать таких вещей. В разработке хватает инструментов и проблем вызванных их обилием. Однако я понимаю почему данный инструмент необходим комьюнити. Уверен и вы поймете, если вспомните что в марте 2020 GitHub/Microsoft поглотили компанию npm. Именно в марте 2020 был написан первый коммит corepack, который тогда назывался pmm. Поэтому я считаю, что corepack это правильный шаг без которого слишком легко оказаться в тупике развития моностэка технологий.
Что нужно знать об Error stack trace?
#nodejs_api

В JavaScript есть встроенный объект Error, который сохраняет stack trace (на русском трассировка стека). Так называется список методов, которые были вызваны до момента, когда в приложении произошло ошибка. Он доступен как error.stack и выглядит так:
Error: Things keep happening!
at /home/user/file.js:525:2
at Frobnicator.refrobulate (/home/user/business-logic.js:424:21)
at Actor.<anonymous> (/home/user/actors.js:400:8)
at increaseSynergy (/home/user/actors.js:701:6)

Плохой практикой является использование throw c литералами или объектами не наследниками Error. У них не будет stack trace.

По умолчанию длина стэка ограничена 10 методами. Параметр можно изменить на уровне кода через Error.stackTraceLimit. На уровне v8 существует флаг --stack-trace-limit. Его можно передать как аргумент или через переменную окружения NODE_OPTIONS=--stack-trace-limit=10

Начиная с 12-ой версии Node.js у нас есть поддержка Async Stack Traces. Пример stack trace:
Error: Oops
at bar (/workspace/test.js:11:9)
at async run (/workspace/test.js:5:3)
Для поддержки этих stack trace рекомендуется делать await перед return. Подробней в nodebestpractices.

Если код скомпилирован с помощью babel или typescript, то правильным будет показывать stack trace для исходного кода, а не скомпилированного. Для этого использовался пакет source-map-support. Под капотом идет использование Error.prepareStackTrace. Однако сейчас Node.js умеет это делать из коробки с помощью флага --enable-source-maps
👍3
Как сделать object deep clone?
#nodejs_api

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

Native JavaScript предлагает два варианта копирования:
– Через spread оператор const copied = { ...original }
– Через Object.assign const copied = Object.assign({}, original)

Эти варианты работают только на первом уровне объекта. Для глубокого копирования необходимо использовать рекурсию или пакеты (см lodash.cloneDeep). В простых случаях можно использовать JSON.parse(JSON.stringify(obj)), но имейте ввиду так можно потерять данные.

Текущий способ для Node.js это использование модуля v8:
const v8 = require('v8');
const copied = v8.deserialize(v8.serialize(original))

Но через год это измениться. Задача настолько часто встречается, что разработчики JS движков придумали structuredClone. Пока внедрено только в Firefox 94 и Node.js v17.
👍2
Как проверить, что строка timezone или encoding?
#nodejs_api

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

function isTimezone(str) {
try {
Intl.DateTimeFormat(undefined, { timeZone: str });
return true;
} catch (ex) {
return false;
}
}

Для проверки кодировки (encoding) можно использовать Buffer.isEncoding.

Собственно цель сегодняшнего рецепта напомнить, что часть buildin modules в #nodejs являются глобальными и их не нужно импортировать.