ЦИТК-795: CodeReview - deepMerge в стрелочном стиле, дополнительные комментарии по коду

This commit is contained in:
Mikhail Chechnev 2026-03-11 15:36:20 +03:00
parent 72feca2a38
commit b2b7d01342

View File

@ -30,8 +30,9 @@ const { SPROTOCOL_HTTP, SPROTOCOL_KAFKA } = require("../models/obj_service"); //
//Глубокое клонирование //Глубокое клонирование
const deepClone = value => { const deepClone = value => {
//Клонируем объект в зависимости от его типа, сохраняя прототипы //Буфер для фиксации "клонированных" элементов исходного объекта
const seen = new WeakMap(); const seen = new WeakMap();
//Функция рекурсивного клонирования (разбирает тип полученного значения и в зависимости от него выполняет клонирование, сохраняя прототипы)
const clone = val => { const clone = val => {
//Примитивы и функции возвращаем как есть //Примитивы и функции возвращаем как есть
if (val === null || typeof val !== "object") return val; if (val === null || typeof val !== "object") return val;
@ -104,10 +105,12 @@ const deepClone = value => {
}); });
return obj; return obj;
}; };
//Выполняем копирование рекурсивно с верхнего уровня
try { try {
//Возвращаем что получилось
return clone(value); return clone(value);
} catch (e) { } catch (e) {
//В случае непредвиденной ошибки формируем информативное исключение //В случае непредвиденной ошибки формируем исключение
const err = new Error("Ошибка глубокого копирования объекта"); const err = new Error("Ошибка глубокого копирования объекта");
err.originalError = e; err.originalError = e;
throw err; throw err;
@ -407,30 +410,33 @@ const getNowString = () => {
}; };
//Глубокое слияние объектов //Глубокое слияние объектов
function deepMerge(...sources) { const deepMerge = (...sources) => {
//Проверка на простой объект (не имеет специального поведения или прототипа)
const isPlainObject = value => Object.prototype.toString.call(value) === "[object Object]"; const isPlainObject = value => Object.prototype.toString.call(value) === "[object Object]";
//Клоникарование значения
const cloneValue = value => { const cloneValue = value => deepClone(value);
return deepClone(value); //Буфер для результата
};
const target = {}; const target = {};
//Обходим входные параметры по очереди
for (const source of sources) { for (const source of sources) {
//Пропускаем непростые объекты - сливать можем только простые
if (!isPlainObject(source)) continue; if (!isPlainObject(source)) continue;
//Обходим пары "ключ-значение"
for (const [key, value] of Object.entries(source)) { for (const [key, value] of Object.entries(source)) {
//Если значение ключа - объект
if (isPlainObject(value)) { if (isPlainObject(value)) {
//Сливаем рекурсивно
if (!isPlainObject(target[key])) target[key] = {}; if (!isPlainObject(target[key])) target[key] = {};
target[key] = deepMerge(target[key], value); target[key] = deepMerge(target[key], value);
} else { } else {
//Примитивы - просто клонируем
target[key] = cloneValue(value); target[key] = cloneValue(value);
} }
} }
} }
//Возвращаем собранный буфер, содержащий все слитые воедино объекты-параметры
return target; return target;
} };
//Глубокое копирование объекта //Глубокое копирование объекта
const deepCopyObject = obj => JSON.parse(JSON.stringify(obj)); const deepCopyObject = obj => JSON.parse(JSON.stringify(obj));
@ -509,27 +515,31 @@ const getURLProtocol = sURL => {
//Обёртывание промиса в таймаут исполнения //Обёртывание промиса в таймаут исполнения
const wrapPromiseTimeout = (timeout, executor) => { const wrapPromiseTimeout = (timeout, executor) => {
//Проверяем входные параметры - должен быть указан ненулевой таймаут и функция-исполнитель промиса
if (!timeout || typeof executor !== "function") { if (!timeout || typeof executor !== "function") {
//Разрешаем сразу, не ожидая
return executor ? executor() : Promise.resolve(); return executor ? executor() : Promise.resolve();
} }
//Параметры прошли проверку, создаем экхемпляр контроллера прерывания асинхронного процесса
const controller = new AbortController(); const controller = new AbortController();
//Подготовим объект для выбрасывания ошибки
const sMessage = `Истёк интервал ожидания (${timeout} мс) завершения асинхронного процесса.`; const sMessage = `Истёк интервал ожидания (${timeout} мс) завершения асинхронного процесса.`;
const timeoutError = new Error(sMessage); const timeoutError = new Error(sMessage);
timeoutError.error = sMessage; timeoutError.error = sMessage;
//Буфер для идентификатора таймера
let timeoutPid; let timeoutPid;
//Создаём промис с взведенным таумером на у казанный таймаут, промис будет "отклонён" по истечении таймера, но перед этим контроллер прерывания выдаст "сигнал" на отмену осинхронного процесса
const timeoutPromise = new Promise((_, reject) => { const timeoutPromise = new Promise((_, reject) => {
timeoutPid = setTimeout(() => { timeoutPid = setTimeout(() => {
controller.abort(timeoutError); controller.abort(timeoutError);
reject(timeoutError); reject(timeoutError);
}, timeout); }, timeout);
}); });
//Создаём промис с функцией-исполнителем асинхронного процесса
const taskPromise = Promise.resolve(executor(controller.signal)); const taskPromise = Promise.resolve(executor(controller.signal));
//Запускаем оба промиса в режиме "гонка" - кто "быстрее" исполнится
return Promise.race([taskPromise, timeoutPromise]).finally(() => { return Promise.race([taskPromise, timeoutPromise]).finally(() => {
//В любом случае не забываем зачистить взведённый таймер
if (timeoutPid) clearTimeout(timeoutPid); if (timeoutPid) clearTimeout(timeoutPid);
}); });
}; };