Настоящий JavaScript
الذهاب إلى القناة على Telegram
Тот самый канал по JavaScript. Личный блог автора - @just_genych По вопросам рекламы или разработки: @g_abashkin
إظهار المزيد6 283
المشتركون
-624 ساعات
+217 أيام
+5930 أيام
جاري تحميل البيانات...
القنوات المماثلة
سحابة العلامات
الإشارات الواردة والصادرة
---
---
---
---
---
---
جذب المشتركين
يونيو '26
يونيو '26
+103
في 0 قنوات
مايو '26
+384
في 0 قنوات
Get PRO
أبريل '26
+83
في 0 قنوات
Get PRO
مارس '26
+71
في 0 قنوات
Get PRO
فبراير '26
+23
في 0 قنوات
Get PRO
يناير '26
+18
في 0 قنوات
Get PRO
ديسمبر '25
+13
في 0 قنوات
Get PRO
نوفمبر '25
+24
في 0 قنوات
Get PRO
أكتوبر '25
+22
في 0 قنوات
Get PRO
سبتمبر '25
+21
في 0 قنوات
Get PRO
أغسطس '25
+20
في 0 قنوات
Get PRO
يوليو '25
+38
في 0 قنوات
Get PRO
يونيو '25
+52
في 0 قنوات
Get PRO
مايو '25
+121
في 0 قنوات
Get PRO
أبريل '25
+88
في 0 قنوات
Get PRO
مارس '25
+363
في 20 قنوات
Get PRO
فبراير '25
+30
في 0 قنوات
Get PRO
يناير '25
+38
في 0 قنوات
Get PRO
ديسمبر '24
+41
في 0 قنوات
Get PRO
نوفمبر '24
+64
في 2 قنوات
Get PRO
أكتوبر '24
+70
في 1 قنوات
Get PRO
سبتمبر '24
+84
في 2 قنوات
Get PRO
أغسطس '24
+70
في 0 قنوات
Get PRO
يوليو '24
+115
في 0 قنوات
Get PRO
يونيو '24
+89
في 4 قنوات
Get PRO
مايو '24
+101
في 0 قنوات
Get PRO
أبريل '24
+121
في 0 قنوات
Get PRO
مارس '24
+132
في 0 قنوات
Get PRO
فبراير '24
+101
في 0 قنوات
Get PRO
يناير '24
+86
في 0 قنوات
Get PRO
ديسمبر '23
+90
في 0 قنوات
Get PRO
نوفمبر '23
+16
في 0 قنوات
Get PRO
أكتوبر '23
+15
في 0 قنوات
Get PRO
سبتمبر '23
+16
في 0 قنوات
Get PRO
أغسطس '23
+24
في 0 قنوات
Get PRO
يوليو '23
+35
في 0 قنوات
Get PRO
يونيو '23
+20
في 0 قنوات
Get PRO
مايو '23
+15
في 0 قنوات
Get PRO
أبريل '23
+264
في 0 قنوات
Get PRO
مارس '23
+17
في 0 قنوات
Get PRO
فبراير '23
+207
في 0 قنوات
Get PRO
يناير '23
+40
في 0 قنوات
Get PRO
ديسمبر '22
+478
في 0 قنوات
Get PRO
نوفمبر '22
+1 627
في 0 قنوات
Get PRO
أكتوبر '22
+45
في 0 قنوات
Get PRO
سبتمبر '22
+451
في 0 قنوات
Get PRO
أغسطس '22
+459
في 0 قنوات
Get PRO
يوليو '22
+1 991
في 0 قنوات
Get PRO
يونيو '22
+1 567
في 0 قنوات
Get PRO
مايو '22
+1 769
في 0 قنوات
Get PRO
أبريل '22
+97
في 0 قنوات
Get PRO
مارس '22
+2 701
في 0 قنوات
| التاريخ | نمو المشتركين | الإشارات | القنوات | |
| 18 يونيو | 0 | |||
| 17 يونيو | 0 | |||
| 16 يونيو | +1 | |||
| 15 يونيو | +1 | |||
| 14 يونيو | +2 | |||
| 13 يونيو | +2 | |||
| 12 يونيو | +11 | |||
| 11 يونيو | +39 | |||
| 10 يونيو | +24 | |||
| 09 يونيو | +17 | |||
| 08 يونيو | 0 | |||
| 07 يونيو | 0 | |||
| 06 يونيو | 0 | |||
| 05 يونيو | +2 | |||
| 04 يونيو | 0 | |||
| 03 يونيو | 0 | |||
| 02 يونيو | +2 | |||
| 01 يونيو | +2 |
منشورات القناة
Утечки памяти в production: как отлавливать то, что не видно в логах
Каждый раз, когда слышу "у нас утечка памяти", первая мысль - кто-то забыл удалить слушатель событий или не очистил setInterval. В production все сложнее: утечки копятся неделями, проявляются при определенной нагрузке, и локально их не воспроизвести.
Вот три рабочих инструмента.
Heap snapshots: как читать между строк
Снимаешь снапшот в Chrome DevTools (Memory -> Take heap snapshot). Сравниваешь два: до и после выполнения подозрительного сценария.
Ищешь объекты, которые:
- Не должны быть в памяти, но есть (старые DOM-ноды с замыканиями)
- Имеют неожиданно много экземпляров (10 000 объектов класса
Widget)
Если видишь Closure с огромным retained size - это замыкание, которое держит ссылку на внешние данные. В production такая утечка часто возникает в SSR, когда обработчик запроса захватывает ссылку на глобальный кэш.
Memory tab: интерактивный детектив
Переходишь на вкладку Memory, выбираешь "Allocation instrumentation on timeline". Записываешь профиль при активной работе пользователя.
Красные пики в столбчатой диаграмме - места, где память выделяется, но не освобождается. Клик по пику покажет стек вызовов в момент аллокации.
Паттерн, который ловил так: React-компонент, при каждом ререндере создавал новый объект-конфигурацию и передавал в children - GC не успевал чистить, через час работы страница весила 200+ МБ.
Автоматизированные паттерны детекции
Ручной анализ хорошо, но в production нужно автоматическое обнаружение. Два подхода.
Performance Observer с проверкой usedJSHeapSize:
new PerformanceObserver((list) => {
const entries = list.getEntries();
if (performance.memory?.usedJSHeapSize > 200_000_000) {
console.warn('Memory warning', performance.memory);
}
}).observe({ type: 'resource', buffered: true });
Только в Chrome с флагом enable-experimental-web-platform-features. Типичная ошибка - использовать это без fallback в Safari или Firefox.
Кастомный мониторинг:
setInterval(() => {
const { usedJSHeapSize, totalJSHeapSize } = performance.memory;
const usagePercent = (usedJSHeapSize / totalJSHeapSize) * 100;
if (usagePercent > 80) {
sendMetric('memory_leak_risk', usedJSHeapSize);
}
}, 60000);
Не используй для продакшена бездумно - performance.memory есть не везде. Оборачивай в try/catch и делай fallback на navigator.userAgent.
Утечки в production - это не баги, а архитектурные проблемы. Если после каждого перехода по роуту память растет на 5-10% и не падает - ищи подписки в эффектах, которые не отписываются при размонтировании. Или глобальные кэши, которые никогда не чистятся.
Вывод: Надежная детекция утечек требует сочетания автоматических инструментов и ручного анализа heap snapshots, а не надежды на GC или банальные логи.| 2 | 🔴 Собеседование на Middle Frontend-разработчика начнётся уже через 2 часа. Переходи в бот за ссылкой: @shortcut_front_bot | 134 |
| 3 | Temporal в JavaScript: даты без production-боли от Date
Date смешивает слишком много понятий: момент времени, локальное представление, таймзону окружения, парсинг строк и мутабельность. В итоге баги часто появляются не в unit-тестах, а в production: на DST-переходах, у пользователей из другой таймзоны или при переносе кода между Node.js и браузером.
Temporal решает это за счёт явного разделения сущностей:
• Temporal.Instant — точный момент времени, удобно хранить в UTC
• Temporal.ZonedDateTime — дата/время + IANA timezone
• Temporal.PlainDate — календарная дата без времени и таймзоны
• Temporal.PlainTime — время без даты
• Temporal.Duration — длительность
• Temporal.PlainDateTime — локальные дата и время без привязки к зоне
Главная идея: не притворяться, что «дата» — это одна универсальная сущность.
Пример с DST:
import { Temporal } from '@js-temporal/polyfill';
const meeting = Temporal.ZonedDateTime.from(
'2024-03-30T12:00:00+01:00[Europe/Berlin]'
);
console.log(meeting.add({ days: 1 }).toString());
// 2024-03-31T12:00:00+02:00[Europe/Berlin]
console.log(meeting.add({ hours: 24 }).toString());
// 2024-03-31T13:00:00+02:00[Europe/Berlin]
Это не одно и то же.
add({ days: 1 }) означает «завтра в это же локальное время».
add({ hours: 24 }) означает «через 24 реальных часа».
На переходе на летнее время день может быть не 24 часа. Temporal делает это различие явным, а Date обычно прячет проблему до момента, когда пользователи начинают жаловаться.
Ещё один важный кейс — несуществующее локальное время:
try {
Temporal.ZonedDateTime.from(
'2024-03-31T02:30[Europe/Berlin]',
{ disambiguation: 'reject' }
);
} catch {
console.log('Такого локального времени нет из-за DST');
}
В ночь перехода на летнее время 02:30 в Berlin может просто не существовать. В Date такие ситуации часто «нормализуются» молча. В Temporal можно явно выбрать стратегию: reject, earlier, later, compatible.
Практические правила для production:
1. Храните точный момент как Instant
Например, событие уже произошло или платёж создан.
2. Храните пользовательскую таймзону отдельно
Не GMT+3, а IANA zone: Europe/Berlin, Asia/Tbilisi, America/New_York.
3. Для бизнес-правил используйте календарные типы
День рождения — это PlainDate, а не Date в полночь UTC.
«Каждый день в 09:00 по Москве» — это локальное время + timezone, а не setInterval(24h).
4. Не заменяйте календарные операции миллисекундами
+ 86400000 — это не всегда «завтра». На DST это может быть 23 или 25 локальных часов.
5. Делайте неоднозначность явной
Если локальное время может не существовать или повторяться, выбирайте поведение явно через disambiguation.
Temporal особенно полезен там, где цена ошибки высокая: расписания, биллинг, бронирования, напоминания, cron-like задачи, SLA, отчёты по локальным дням.
Важно: поддержка Temporal зависит от runtime. Для production сейчас обычно используют polyfill @js-temporal/polyfill и постепенно готовятся к нативной поддержке.
Коротко: Date хорош для простых timestamp-операций, но плохо моделирует реальные бизнес-даты. Temporal заставляет выбрать правильную модель времени — и именно поэтому снижает количество багов на таймзонах и DST. | 132 |
| 4 | Iterator Helpers в JavaScript: ленивые пайплайны без промежуточных массивов и ловушка одноразовых итераторов
Iterator Helpers добавляют к итераторам map, filter, take, reduce, toArray и другие методы в стиле массивов. Это полезно в API clients, Node.js-сервисах, SSR и build tooling, но частая ошибка - воспринимать iterator pipeline как переиспользуемую коллекцию.
Ленивость вместо временных массивов
const emails = users
.values()
.filter(user => user.active)
.map(user => user.email)
.take(10)
.toArray();
Здесь не создаются массивы после filter и map. Элементы проходят цепочку по одному: user -> filter -> map -> take.
Это выигрывает, когда данных много, нужен только префикс результата или источник потенциально бесконечный:
function* numbers() {
let i = 0;
while (true) yield i++;
}
const result = numbers()
.filter(n => n % 2 === 0)
.map(n => n * n)
.take(5)
.toArray();
Подводный камень: итератор одноразовый
const iterator = [1, 2, 3]
.values()
.map(x => x * 2);
iterator.toArray(); // [2, 4, 6]
iterator.toArray(); // []
Это не баг, а модель курсора. Если часть данных уже прочитана через next(), пайплайн продолжит с текущей позиции.
Практический совет
Не передавайте iterator pipeline как “коллекцию” между модулями:
const activeUsers = users
.values()
.filter(user => user.active);
sendEmails(activeUsers);
renderUsers(activeUsers); // может быть пусто
Если результат нужен несколько раз, материализуйте его:
const activeUsers = users
.values()
.filter(user => user.active)
.toArray();
Или отдавайте фабрику нового итератора:
const activeUsers = () =>
users.values().filter(user => user.active);
Терминальные операции запускают проход
map, filter, take ленивые. toArray, reduce, forEach, some, every, find реально потребляют итератор.
Вывод:
Iterator Helpers хороши для читаемых и экономных пайплайнов, но надежная архитектура требует явно решать, где итератор одноразовый, а где нужна материализованная коллекция. | 184 |
| 5 | Explicit Resource Management в JS/TS: using, await using и безопасный cleanup в async-коде без try/finally в каждом месте
В JS/TS появляется детерминированное освобождение ресурсов: не когда GC соберет объект, а при выходе из scope. В production это важно для Node.js-сервисов, SSR, SDK, API clients и shared libraries, где часто забывают вызвать close, release, rollback или unsubscribe.
Идея
Ресурс реализует протокол, а язык вызывает cleanup сам:
* using - синхронный [Symbol.dispose]()
* await using - асинхронный [Symbol.asyncDispose]()
await using дожидается cleanup, поэтому подходит для DB connection, Redis lock, stream, socket, transaction.
Production-паттерн: транзакция
class Tx {
committed = false;
async commit() {
await db.commit();
this.committed = true;
}
async [Symbol.asyncDispose]() {
if (!this.committed) await db.rollback();
}
}
async function createOrder(input: OrderInput) {
await using tx = await db.beginTransaction();
const order = await tx.orders.create(input);
await tx.audit.log("order_created", order.id);
await tx.commit();
return order;
}
Если audit.log выбросит ошибку, транзакция будет откатана без ручного try/finally в каждом use case.
Важные детали
* dispose вызывается в обратном порядке объявления
* cleanup выполняется при return и throw
* ошибки из основного кода и dispose не теряются, используется SuppressedError
* это не замена GC: GC про память, using - про внешние ресурсы
Практический совет
Для библиотечного API проектируйте не контракт "не забудь вызвать .close()", а явный протокол [Symbol.dispose] / [Symbol.asyncDispose]. В TypeScript синтаксис поддерживается с TS 5.2.
Предупреждение: если cleanup возвращает Promise, обычный using не подходит - используйте await using.
Вывод:
using и await using делают жизненный цикл ресурсов явным, снижают риск утечек и улучшают надежность async-кода без разрастания try/finally. | 188 |
| 6 | 🔍Тестовое собеседование с руководителем Frontend-разработки в этот четверг
18 июня(в четверг!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Frontend-разработчика.
Как это будет:
📂 Виталий Черков, руководитель группы Frontend разработки с опытом 8+ лет, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Виталий будет комментировать каждый ответ респондента, чтобы дать понять, чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Виталию
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Frontend-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_front_bot
Реклама.
О рекламодателе. | 230 |
| 7 | Web Locks API в браузере: cross-tab mutex для refresh token, миграций и защиты от гонок
Когда одна сессия открыта в нескольких вкладках, frontend, SPA, SSR-клиенты и SDK начинают делить auth state, storage и кэш. Частая ошибка - защищать refresh token только in-memory single-flight: между вкладками он не работает.
Refresh token как критическая секция
Если 5 вкладок одновременно получили 401, они могут отправить несколько refresh-запросов одним token, получить invalid_grant и перетереть свежие токены старыми. Web Locks API дает mutex на один origin:
type Tokens = {
accessToken: string;
refreshToken: string;
};
const LOCK = 'auth:refresh-token';
async function ensureAccessToken(): Promise<string> {
return navigator.locks.request(LOCK, async () => {
const latest: Tokens = await loadTokens();
if (!isExpiring(latest)) {
return latest.accessToken;
}
const next = await refreshTokens(latest.refreshToken);
await saveTokens(next);
return next.accessToken;
});
}
Важный паттерн
После входа в lock нужно перечитать storage. Пока вкладка ждала, другая уже могла обновить токены. Практический совет: внутри критической секции всегда делайте double-check precondition, а не запускайте refresh сразу после ожидания.
Где еще полезно
Web Locks хорошо ложится на миграции IndexedDB/localStorage, одноразовую инициализацию кэша, защиту записи в общий browser storage и координацию фоновых задач между вкладками.
Ограничения
Lock работает только в пределах origin. Это не distributed lock и не замена серверной идемпотентности. Критическая секция должна быть короткой; для сетевого refresh ставьте timeout. Если navigator.locks нет, нужен fallback через BroadcastChannel, storage lease или серверную защиту.
Вывод:
Web Locks API полезен там, где несколько вкладок делят runtime-состояние: он снижает риск гонок, но не отменяет серверные гарантии. | 261 |
| 8 | День сурка frontend-разработчика
Зарплата стоит, скучные задачи день за днем, календарь забит созвонами, которые не влияют вообще ни на что.
Откликаешься на вакансии, а в ответ тишина либо какие-то мутные конторы. На собесах вместо нормальной оценки навыков цирк с алгоритмами на скорость, как будто ты на олимпиаде, а не работу ищешь.
И самое неприятное, пока ты варишься в этом болоте, кто-то спокойно проходит собесы и уходит в Яндекс, VK или на хорошую Валютную удаленку без лишней драмы.
Есть классные проекты и сильные команды, где разработчиков действительно ценят, дают расти, поддерживают развитие и платят достойно и ты можешь туда попасть!
👋 Меня зовут Тихон, привет! Я — действующий Frontend-разработчик и ментор. Я за руку довожу до оффера на хорошую позицию в Big Tech и сопровождаю на испытательном сроке.
Также из учеников я собираю комьюнити, где уже более 220 frontend-разработчиков🫂
А в своем канале:
👉Объясняю, как проходить HR-фильтр и превращать отклики в реальные приглашения
👉Помогаю найти мотивацию, борюсь убеждениями, которые мешают развиваться
👉На примерах объясняю, как проходить собеседования, включая техничку
👉Разбираю резюме и делюсь лайфхаками, например как аккуратно “пинговать” рекрутеров
А еще регулярно публикую полезные материалы:
▪️Задачи, на которых валяться кандидаты
▪️База по микрофронтам
▪️Подборка из 100+ каналов с вакансиями для разработчиков
▪️100 вопросов, которые точно помогут тебе на собеседовании
▪️Чек лист проверки своего резюме
А еще у меня множество успешных кейсов и отзывов, найти их можно в канале.
Реклама, erid: 2W5zFJeWaNd ИП Галактионов Тихон Витальевич, ИНН 771618975809 | 236 |
| 9 | Promise.withResolvers() в production: ручное завершение промисов без deferred-антипаттернов и утечек
Promise.withResolvers() полезен на границе event-based API и async/await: WebSocket, Worker, IPC, SDK, API clients, Node.js-сервисы. Частая ошибка - считать его безопасной заменой любому deferred и хранить resolve/reject где попало.
Что это даёт
const { promise, resolve, reject } =
Promise.withResolvers<T>();
Это тот же внешний resolve/reject, но без let, definite assignment и самодельного executor-шаблона. API стал чище, но lifecycle всё ещё ваша ответственность.
Где применять
* ожидание одного события;
* bridge callback/event API в Promise;
* request/response поверх WebSocket, Worker или IPC;
* внутренняя очередь producer/consumer.
Не стоит передавать resolve через слои, класть его в глобальное состояние или создавать promise без timeout/cancel path.
Production-паттерн
type Message = { id: string; payload: unknown };
type Waiter = {
resolve: (m: Message) => void;
reject: (e: Error) => void;
timeout: ReturnType<typeof setTimeout>;
};
const pending = new Map<string, Waiter>();
function waitForMessage(id: string, ms = 5000) {
const { promise, resolve, reject } =
Promise.withResolvers<Message>();
const timeout = setTimeout(() => {
pending.delete(id);
reject(new Error(Message timeout: ${id}));
}, ms);
pending.set(id, { resolve, reject, timeout });
return promise.finally(() => {
clearTimeout(timeout);
pending.delete(id);
});
}
Что важно
У promise есть owner - waitForMessage(). Есть timeout, очистка через finally(), а resolve/reject не утекают наружу. Иначе Map будет удерживать замыкания, таймеры и payload.
Практический совет: если у вызывающего кода есть свой lifecycle, добавьте AbortSignal и снимайте listener в finally().
Вывод:
Promise.withResolvers() стоит использовать только там, где явно определены owner, timeout/cancel path и очистка ресурсов. | 226 |
| 10 | Dual package hazard в ESM/CJS: conditional exports без двух копий singleton-состояния в одном Node.js-процессе
Это важно для SDK, API clients, SSR, Node.js-сервисов и shared libraries. Частая ошибка - считать, что одинаковый TS-код в .mjs и .cjs даст общий runtime.
Где ломается
const cjs = require('@acme/sdk')
const esm = await import('@acme/sdk')
Если require попал в dist/index.cjs, а import - в dist/index.mjs, Node загрузит два разных модуля.
Итог:
* два singleton-инстанса
* две registry/cache/map
* разные подключения
* сломанный instanceof
* расходящееся состояние
Рабочая схема
Один runtime source of truth, второй формат - только фасад. Например, состояние живет в core.cjs, а ESM лишь импортирует его:
// package.json
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/core.cjs"
}
}
// core.cjs
const registry = new Registry()
module.exports = { Registry, registry }
// index.mjs
import api from './core.cjs'
export const Registry = api.Registry
export const registry = api.registry
export default api
Теперь require и import смотрят на один singleton:
cjs.registry === esm.registry // true
cjs.Registry === esm.Registry // true
Типичная ошибка
Не делайте две независимые реализации:
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
Если оба файла содержат состояние, hazard почти гарантирован. Conditional exports только маршрутизируют загрузку, но не объединяют объекты в памяти.
Практический чеклист
* закрывайте deep imports вроде @acme/sdk/dist/index.mjs
* для каждого subpath, например @acme/sdk/cache, повторяйте ту же схему
* для чистых функций риск ниже, для DI, логгеров, метрик, БД-клиентов и кэшей - критичен
Вывод:
Dual ESM/CJS-пакету нужен один общий runtime-модуль и фасадные entrypoint, а не две сгенерированные копии реализации. | 261 |
| 11 | Совет на ближайшие годы — изучайте ВАЙБ-КОДИНГ
ИИ уже пишет код, чинит баги, генерирует тесты, документацию и помогает запускать продукты быстрее, чем это делали классические команды разработки. И это уже не "будущее когда-нибудь", а реальность, которая меняет рынок уже сегодня
И те, кто научится вайбкодить сейчас, будут увереннее конкурировать на рынке и зарабатывать больше тех, кто по-прежнему делает всё вручную.
Стартовать с нуля поможет канал Вайб-кодинг. Там ребята круглосуточно мониторят более 320 российских и зарубежных источников и публикуют только главное: релизы, инструменты, гайды, курсы и практические кейсы.
Подписывайтесь, нас уже 45 тысяч: @vibecoding_tg | 307 |
| 12 | AsyncLocalStorage в Node.js: request-scoped контекст для логов, трассировки и транзакций без prop drilling
В production это нужно в Node.js-сервисах, SSR, API integration и backend-for-frontend слоях, где requestId, traceId, tenant или транзакция должны проходить через async-код. Частая ошибка - протаскивать ctx через десятки методов или, наоборот, прятать в нём бизнес-состояние.
Как это выглядит
AsyncLocalStorage использует async_hooks и привязывает store к async execution flow: promise, timeout, I/O callback и большинству стандартных API Node.js.
type Ctx = { requestId: string; traceId?: string; tx?: unknown };
const als = new AsyncLocalStorage<Ctx>();
app.use((req, _res, next) => {
als.run({
requestId: req.headers['x-request-id']?.toString() ?? randomUUID(),
traceId: req.headers.traceparent?.toString(),
}, next);
});
const getCtx = () => als.getStore();
function logInfo(msg: string, meta = {}) {
const c = getCtx();
logger.info({
requestId: c?.requestId,
traceId: c?.traceId,
...meta,
}, msg);
}
Теперь сервисный код не знает про HTTP middleware и req, но логи автоматически получают correlation metadata.
Транзакции без протаскивания tx
Для DB слоя можно делать withTransaction(), который запускает als.run({ ...ctx, tx }, fn), а getDbExecutor() возвращает ctx.tx ?? db. Trade-off хороший: меньше шума в API сервисов, но граница ответственности остаётся инфраструктурной, а не бизнесовой.
Где границы
* не заменяйте аргументы функции: бизнес-данные передавайте явно;
* не кладите в store req, res, большие payload или ORM graph;
* context не пересекает process boundary: worker threads, очереди, cron jobs и другие сервисы требуют явной передачи ids;
* нестандартные callback-based библиотеки могут разорвать async chain.
Практическое правило: используйте AsyncLocalStorage для логов, tracing, tenant/user metadata, аудита и текущей транзакции, а не для скрытого глобального состояния.
Вывод:
AsyncLocalStorage полезен, когда request-scoped инфраструктурный контекст улучшает observability и DX, но не размывает явные границы бизнес-логики. | 312 |
| 13 | АЙТИШНИКИ БЕСПЛАТНОЕ ОБУЧЕНИЕ сборник курсов, инструментов и книг
Проект «TERMINAL» стал крупнейшей библиотекой бесплатного образования. В одном канале собраны курсы, книги, полезные инструменты и практические тренажёры для всех разработчиков
🎓 Практические курсы и задания
🪽 Книги и статьи известных авторов
😮💨 Полезные инструменты и ресурсы
🌟 IT-новости и инсайды
Обучение по всем направлениям: SQL, Python, Frontend, PHP, C++, Golang, GIT, Linux, QA, Java, Vibe-coding, Infosec и др.
Ценишь знания, подпишись: Terminal_tg | 281 |
| 14 | AbortSignal.any() и AbortSignal.timeout(): единая отмена fetch, таймеров и async-операций в production
Promise.race([fetch(), timeout]) часто маскирует проблему: ждать перестали, но работа могла не остановиться. В SPA, SSR, Node.js-сервисах, SDK и API clients это приводит к висящим запросам, таймерам и retry/backoff ниже по стеку.
Собирайте отмену вокруг AbortSignal
AbortSignal.timeout(ms) сам отменится по таймауту.
AbortSignal.any([...signals]) отменится, когда отменится любой входной signal.
Так в один контракт попадают:
* caller отменил операцию
* истек timeout
* клиент закрыл соединение
* сервис уходит в graceful shutdown
const shutdown = new AbortController();
async function loadUser(id: string, opts: { signal?: AbortSignal } = {}) {
const signal = AbortSignal.any([
AbortSignal.timeout(1500),
shutdown.signal,
...(opts.signal ? [opts.signal] : []),
]);
signal.throwIfAborted();
const res = await fetch(`https://api.example.com/users/${id}`, { signal });
await sleep(100, signal); // backoff тоже отменяем
const data = await res.json();
signal.throwIfAborted();
return data;
}
Типичная ошибка
Не останавливайтесь на fetch. Один и тот же signal стоит передавать в retry, polling, очереди, sleep/timer helpers и свои async-функции. Иначе верхний слой "отменился", а нижний продолжает держать ресурсы.
Практические нюансы
* AbortSignal одноразовый: если aborted === true, нужен новый controller или timeout
* AbortSignal.any() - это fan-in, а не fan-out: он не отменяет исходные контроллеры
* смотрите на reason: timeout часто дает TimeoutError, abort - AbortError или ваш reason
* при обертках над setTimeout, stream, listener или socket чистите ресурсы при abort
Вывод:
Отмена должна быть частью контракта async-функции, а не локальным Promise.race на краю системы. | 398 |
| 15 | Нейросети, IT и AI — в одной папке
💬 С коллегами собрали новые каналы про:
💠 промпты для нейросетей и готовые решения
💠 AI-фотосессии, генерация изображений и контента
💠 новости искусственного интеллекта без лишнего шума
💠 применение AI в работе, бизнесе и повседневной жизни
💠 Python, JavaScript, Data Science и системный анализ
💠 вакансии и возможности для специалистов в IT
Посмотреть и подписаться тут 👉 https://t.me/addlist/c_rbhnzprbAwMmFi
💌 Добавить свой канал в папку | 284 |
| 16 | Страшная тайна российского айти
✖️ xCode Journal | 483 |
| 17 | Хватит гадать — DeepSeek за тебя уже всё решил 🐳
* Сейчас все только про Claude, но я перешёл на DeepSeek и не жалею. Бесплатно, контекст 1 млн токенов — закинул целую книгу, помнит всё. Код пишет отлично, а рассуждения (Reasoning) выдают логику, как у архитектора.
Решил протестировать агентский режим на задаче, которую вечно откладывал — собрать чистое инфополе с нуля. Чтобы не перебирать паблики вручную, зашёл через функцию похожих каналов в Telegram.
Скормил DeepSeek ссылки на качественных авторов по IT и AI, которых читаю сам, и попросил проанализировать сотни рекомендаций. Агент изучил контент на каналах и оставил только тех, кто делится практическим опытом по: AI-воркфлоу, автоматизации, вайб-кодингу, промт-инжинирингу, RAG-syst. нейрогенерации и др.
DeepSeek собрал полезную подборку экспертов в одну папку. Делюсь списком — внутри только полезный контент про IT & AI.
🔗Забирай в один клик: 👉 https://t.me/addlist/FYyQj91I8jJiMzg0 | 376 |
| 18 | Variadic tuple types — сложные сигнатуры без боли
До variadic tuple types
многие сложные сигнатуры в TypeScript
выглядели как наказание.
Особенно:
👉 curry
👉 compose
👉 middleware
👉 typed event emitter
👉 любые функции с «прокинь аргументы дальше»
Приходилось писать overload на overload
и дублировать типы вручную.
Как было раньше
Обычно появлялись:
👉 overload на overload
👉 ручные tuple-типы
👉 тонны дублирования
Типы быстро превращались
в нечитаемую простыню.
Что изменили variadic tuples
С их появлением стало намного проще
работать с остаточными аргументами на уровне типов.
Например:
type Fn<T extends unknown[]> =
(...args: [...T]) => void
Или собирать сигнатуры:
type Append<Args extends unknown[], Arg> =
[...Args, Arg]
Типы наконец научились нормально работать
с «переменным количеством аргументов».
Почему это важно
На практике это одна из тех TS-фич,
которые реально упростили жизнь библиотекам.
Без variadic tuples:
👉 Redux middleware typings
👉 router APIs
👉 compose/curry utilities
были бы ещё страшнее.
Где начинается тёмная магия
Проблемы начинаются,
когда variadic tuples комбинируют с:
👉 infer
👉 recursive types
👉 conditional types
Типовая система очень быстро
превращается в тёмный лес.
IDE начинает тормозить,
ошибки становятся нечитаемыми,
а compile time — расти.
Главная мысль
Variadic tuple types —
это действительно мощная фича.
Главное —
вовремя остановиться
и не превратить типы в отдельный язык программирования. | 489 |
| 19 | Стажировки и вакансии для JavaScript разработчиков
- Вакансии которых нет на джоб-агрегаторах
- Только прямые контакты HR в Telegram
@jobs_js_fronted для фронтов
@jobs_js_back для бека
Пока другие листают джоб-сайты — ты уже пишешь HR в Telegram. | 320 |
| 20 | Стажировки и вакансии для JavaScript разработчиков
- Вакансии которых нет на джоб-агрегаторах
- Только прямые контакты HR в Telegram
@jobs_js_fronted для фронтов
@jobs_js_back для бека
Пока другие листают джоб-сайты — ты уже пишешь HR в Telegram. | 235 |
متاح الآن! بحث تيليغرام 2025 — أهم رؤى العام 
