JavaScript Ready | Программирование
8.32K subscribers
1K photos
60 videos
537 links
Авторский канал по разработке на JavaScript.
Ресурсы, обучения, задачи, шпаргалки.
Ежедневно информация пополняется!

Cотрудничество: @energy_c
Download Telegram
📱 Event Sourcing: состояние как история событий!

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

В этом посте:
• Разберём, почему в Event Sourcing состояние становится производным;

• Отделим команды от событий и поймём их разные роли;

• Реализуем восстановление состояния через последовательный replay событий;

• Разберём, почему события становятся источником истины, а не просто журналом изменений.


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

📣 JS Ready | #гайд
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍6❤3
Получаем только изменённые поля объекта!

При изменении состояния приложения можно сравнить предыдущую и текущую версию объекта, чтобы определить только обновлённые значения. Такой diff позволяет отделить изменившиеся поля от тех, которые остались без изменений, и использовать только актуальные данные для дальнейшей обработки.
const oldUser = {
name: "Alex",
age: 30,
role: "developer"
};

const newUser = {
name: "Alex",
age: 31,
role: "developer"
};


Для получения изменённых полей можно преобразовать объект в массив пар через Object.entries(), отфильтровать только отличающиеся значения и собрать новый объект обратно с помощью Object.fromEntries().
const changes = Object.fromEntries(
Object.entries(newUser)
.filter(([key, value]) =>
!Object.is(value, oldUser[key])
)
);

console.log(changes);


В результате получится объект, содержащий только изменённые поля:
{
age: 31
}


Такой подход применяется при обновлении состояния во frontend-приложениях, работе с формами и подготовке частичных изменений для API через PATCH запросы.

Вместо отправки всей структуры можно передать только те значения, которые действительно были изменены.
const hasChanges =
Object.keys(changes).length > 0;

console.log(hasChanges);


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

Важно учитывать, что данный способ выполняет только поверхностное сравнение (shallow diff) и проверяет значения первого уровня объекта.
const first = {
profile: {
name: "Alex"
}
};

const second = {
profile: {
name: "Alex"
}
};

console.log(first.profile === second.profile);

// false


Вложенные объекты сравниваются по ссылке, поэтому одинаковая структура внутри разных объектов не делает их равными. Для таких случаев требуется использовать глубокое сравнение (deep comparison) или отдельную логику построения diff.

🔥 Также при реализации реальных обновлений через PATCH нужно отдельно учитывать удаление полей, так как простой shallow diff определяет только добавленные и изменённые значения.

📣 JS Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤7👍4🤝1
📂 Напоминалка для работы с async/await!

Например, await приостанавливает выполнение текущей async-функции до завершения Promise, а try...catch позволяет удобно обрабатывать возникающие ошибки.

На картинке — основной синтаксис async/await, принцип его работы, сравнение с цепочками .then() и другие полезные моменты.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤8👍7
Склеивайте множество синхронных обновлений в одно!

Иногда функция может вызываться десятки раз за один проход синхронного кода.

Можно накопить изменения и запланировать обработку один раз:
const pending = new Set();

function update(id) {
pending.add(id);
scheduleFlush();
}


queueMicrotask() выполнит flush после текущего синхронного кода, но до перехода к следующей macrotask:
function flush() {
scheduled = false;

process([...pending]);
pending.clear();
}


Теперь даже серия вызовов обработается одной пачкой:
update(1);
update(2);
update(3);

// process([1, 2, 3]) — один раз


🔥 Такой подход объединяет множество быстрых обновлений в одну обработку, уменьшая лишние вычисления и нагрузку. Что крайне полезно для событий и часто обновляемых данных.

📣 JS Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤6👍5
📂 Напоминалка по деструктуризации!

Например, const [a, b] = arr позволяет получить значения из массива, а const { name, age } = user — извлечь нужные свойства из объекта.

На картинке — основные способы деструктуризации массивов и объектов, которые пригодятся в повседневной разработке.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍8❤3🤝2
Почему Array.sort() может создать баг далеко от места сортировки!

С sort() проблема не в самой сортировке, а в скрытой мутации. Вызов выглядит как обычное получение отсортированного результата, хотя одновременно изменяет сам исходный массив:
const scores = [30, 10, 20];

const sorted = scores.sort(
(a, b) => a - b
);

console.log(scores);
// [10, 20, 30]


sort() сортирует массив непосредственно и возвращает ту же ссылку. Поэтому изменение увидит не только код, работающий с sorted, но и любой код, который уже хранит ссылку на исходный массив:
const scores = [30, 10, 20];
const cached = scores;

scores.sort((a, b) => a - b);

console.log(cached);
// [10, 20, 30]


Особенно неприятно это становится на границе функций. По названию getSortedUsers() можно ожидать получение нового результата, но реализация незаметно меняет массив вызывающего кода:
function getSortedUsers(users) {
return users.sort(
(a, b) => a.age - b.age
);
}


В результате функция имеет побочный эффект, который не очевиден из места её вызова. Это уже может затронуть состояние, кэш, мемоизацию или другой код, использующий тот же массив:
const users = getUsers();
const original = users;

getSortedUsers(users);

console.log(original === users);
// true


Классический способ избежать этого — сначала создать новый массив и сортировать уже его. Такой код работает, но копирование приходится выражать отдельной операцией:
const sorted = [...scores].sort(
(a, b) => a - b
);

console.log(scores);
// [30, 10, 20]


toSorted() делает намерение явным: получить новый массив с другим порядком элементов, не изменяя исходный. При этом возвращается уже другая ссылка:
const sorted = scores.toSorted(
(a, b) => a - b
);

console.log(scores);
// [30, 10, 20]

console.log(scores === sorted);
// false


Важно, что копируется именно массив. Объекты внутри него не клонируются, поэтому обе коллекции по-прежнему содержат ссылки на одни и те же объекты:
const users = [
{ name: "Alex", age: 30 },
{ name: "Bob", age: 20 },
];

const sorted = users.toSorted(
(a, b) => a.age - b.age
);

console.log(
sorted[0] === users[1]
);
// true


Поэтому изменение самого объекта будет видно через обе коллекции:
sorted[0].age = 25;

console.log(users[1].age);
// 25


Похожая логика есть у toReversed() и toSpliced(): они позволяют выполнить соответствующую операцию без изменения исходного массива, в отличие от мутирующих reverse() и splice().

🔥 Поэтому выбор между sort() и toSorted() — это не просто вопрос синтаксиса, это выбор контракта операции: изменить существующий массив или получить новый, сохранив исходный. Чем больше у массива потребителей, тем важнее эта разница.

📣 JS Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍6🤝4❤1
📂 Шпаргалка по JavaScript filter()!

Метод filter() используется для отбора элементов массива по заданному условию. Результатом выполнения является новый массив, при этом исходный массив не изменяется.

На изображении — синтаксис метода, аргументы callback-функции и примеры фильтрации чисел и объектов.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥5🤝3
Почему JavaScript может изменить большой ID из API

Числовой идентификатор может быть корректно сформирован на сервере, но изменить значение после преобразования в js Number.

Причина заключается в модели представления чисел: Number соответствует IEEE 754 binary64 и не способен точно представить все целые числа произвольного размера:
const id = 9007199254740993;

console.log(id);

// 9007199254740992


Граница гарантированно точного представления целых чисел определяется константой Number.MAX_SAFE_INTEGER:
console.log(Number.MAX_SAFE_INTEGER);

// 9007199254740991

console.log(
Number.isSafeInteger(9007199254740991)
);

// true


Безопасный диапазон составляет от -(2^53 - 1) до 2^53 - 1. За его пределами проблема заключается не просто в том, что число становится «слишком большим»: соседние математические целые уже не обязаны иметь разные представления в Number. Поэтому два разных исходных значения могут оказаться одним и тем же значением javascript:
const a = 9007199254740992;
const b = 9007199254740993;

console.log(a === b);

// true


Для точных операций с большими целыми в js существует BigInt. Суффикс n создаёт значение непосредственно как BigInt, без промежуточного преобразования в Number:
const id = 9007199254740993n;

console.log(id + 1n);

// 9007199254740994n


С API возникает дополнительная граница — десериализация JSON. Сам JSON может содержать последовательность цифр 9007199254740993 как число, но стандартный JSON.parse() преобразует JSON-число в JavaScript Number.

Если значение находится за пределами безопасного целочисленного диапазона, точность теряется уже во время этого преобразования:
const data = JSON.parse(
'{"id":9007199254740993}'
);

console.log(data.id);

// 9007199254740992


После такого парсинга восстановить исходный ID по полученному Number в общем случае невозможно: информация о первоначальном целом уже потеряна.

Поэтому идентификаторы, над которыми не предполагается арифметика, надёжнее передавать в JSON как строки. Это сохраняет исходную последовательность цифр независимо от числовых ограничений JavaScript-клиента:
const data = JSON.parse(
'{"id":"9007199254740993"}'
);

console.log(data.id);

// "9007199254740993"


Если значение всё же требуется использовать в точных целочисленных вычислениях, строку можно явно преобразовать в BigInt уже после получения ответа API:
const data = JSON.parse(
'{"id":"9007199254740993"}'
);

const id = BigInt(data.id);

console.log(id + 1n);

// 9007199254740994n


🔥 Числовая природа ID не означает, что его следует представлять типом Number. Если идентификатор потенциально превышает Number.MAX_SAFE_INTEGER, его представление становится частью корректности API-контракта: строка сохраняет ID без потерь при передаче, а BigInt подходит там, где действительно требуется точная целочисленная арифметика.

📣 JS Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍8🔥6
📱 Web Workers — выполнение кода в отдельном потоке!

Разбираем Web Workers, с помощью которых можно выполнять ресурсоёмкие задачи вне основного UI-потока: запускать фоновые вычисления, передавать данные между main thread и Worker, получать результаты, обрабатывать ошибки и управлять завершением Worker. Используется для тяжёлых вычислений и обработки больших объёмов данных без блокировки интерфейса.

📣 JS Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍9🔥5
Выполняйте независимые операции без эффекта домино!

Если несколько операций не зависят друг от друга, Promise.all() может создать лишнюю связанность: одна ошибка отклоняет общий Promise, хотя остальные задачи продолжат выполняться и могут успешно завершиться.
const users = await Promise.all(
ids.map(id => loadUser(id))
);


Для независимых задач используйте Promise.allSettled() — он дождётся каждой и сохранит результат отдельно:
const results = await Promise.allSettled(
ids.map(id => loadUser(id))
);

const successful = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);


Что полезно для пакетной отправки, фоновых задач и загрузки независимых данных: ошибка одной операции не скрывает успешные результаты остальных.

🔥 Если операции независимы, не позволяйте одной ошибке скрывать результат остальных — обрабатывайте каждую отдельно.

📣 JS Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍5🤝3❤1
❤️ Немного ностальгии и чистого JS в статье на Хабре: «TAMA-90: тамагочи из 90-х своими руками на JavaScript»!

В этой статье:
• Показывается, как с нуля собрать браузерного тамагочи на HTML, CSS и JavaScript — без фреймворков, npm и сборщиков;
• Разбираются игровое состояние, жизненный цикл питомца, случайные события, мини-игра, коллекция и сохранение прогресса через localStorage;
• Объясняется, как рисовать пиксельных персонажей программно через Canvas и создавать звуки через Web Audio API без готовых изображений и аудиофайлов.

🔊 Продолжайте читать на Habr!


📣 JS Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥7👍5🤝2
📂 Шпаргалка по технологиям взаимодействия систем!

Например, REST используют для построения API, WebSocket — для двустороннего обмена данными, а gRPC — для взаимодействия между сервисами.

На картинке — основные технологии и подходы, с которыми регулярно сталкиваются разработчики: REST, Webhooks, GraphQL, SOAP, WebSocket, gRPC, MQTT, AMQP, SSE, EDI и EDA. Кратко и наглядно показано, чем они отличаются, как работают и для каких задач применяются.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤5🤝5🔥2
📂 Как работает HTTP и HTTPS!

HTTP передаёт данные между клиентом и сервером без шифрования, а HTTPS добавляет TLS — он аутентифицирует сервер, согласовывает ключи и защищает дальнейший обмен данными шифрованием.

На картинке наглядно показано, чем отличается обычное HTTP-соединение от HTTPS: от TCP-handshake до установки защищённого соединения и передачи данных.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤5🔥4🤝3