From b2b7d01342bb8bed8702b55155c9364c97b67a67 Mon Sep 17 00:00:00 2001 From: Mikhail Chechnev Date: Wed, 11 Mar 2026 15:36:20 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A6=D0=98=D0=A2=D0=9A-795:=20CodeReview=20-?= =?UTF-8?q?=20deepMerge=20=D0=B2=20=D1=81=D1=82=D1=80=D0=B5=D0=BB=D0=BE?= =?UTF-8?q?=D1=87=D0=BD=D0=BE=D0=BC=20=D1=81=D1=82=D0=B8=D0=BB=D0=B5,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=80=D0=B8=D0=B8=20=D0=BF=D0=BE=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/utils.js | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/core/utils.js b/core/utils.js index 1bc6437..87bffe4 100644 --- a/core/utils.js +++ b/core/utils.js @@ -30,8 +30,9 @@ const { SPROTOCOL_HTTP, SPROTOCOL_KAFKA } = require("../models/obj_service"); // //Глубокое клонирование const deepClone = value => { - //Клонируем объект в зависимости от его типа, сохраняя прототипы + //Буфер для фиксации "клонированных" элементов исходного объекта const seen = new WeakMap(); + //Функция рекурсивного клонирования (разбирает тип полученного значения и в зависимости от него выполняет клонирование, сохраняя прототипы) const clone = val => { //Примитивы и функции возвращаем как есть if (val === null || typeof val !== "object") return val; @@ -104,10 +105,12 @@ const deepClone = value => { }); return obj; }; + //Выполняем копирование рекурсивно с верхнего уровня try { + //Возвращаем что получилось return clone(value); } catch (e) { - //В случае непредвиденной ошибки формируем информативное исключение + //В случае непредвиденной ошибки формируем исключение const err = new Error("Ошибка глубокого копирования объекта"); err.originalError = e; 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 cloneValue = value => { - return deepClone(value); - }; - + //Клоникарование значения + const cloneValue = value => deepClone(value); + //Буфер для результата const target = {}; - + //Обходим входные параметры по очереди for (const source of sources) { + //Пропускаем непростые объекты - сливать можем только простые if (!isPlainObject(source)) continue; - + //Обходим пары "ключ-значение" for (const [key, value] of Object.entries(source)) { + //Если значение ключа - объект if (isPlainObject(value)) { + //Сливаем рекурсивно if (!isPlainObject(target[key])) target[key] = {}; target[key] = deepMerge(target[key], value); } else { + //Примитивы - просто клонируем target[key] = cloneValue(value); } } } - + //Возвращаем собранный буфер, содержащий все слитые воедино объекты-параметры return target; -} +}; //Глубокое копирование объекта const deepCopyObject = obj => JSON.parse(JSON.stringify(obj)); @@ -509,27 +515,31 @@ const getURLProtocol = sURL => { //Обёртывание промиса в таймаут исполнения const wrapPromiseTimeout = (timeout, executor) => { + //Проверяем входные параметры - должен быть указан ненулевой таймаут и функция-исполнитель промиса if (!timeout || typeof executor !== "function") { + //Разрешаем сразу, не ожидая return executor ? executor() : Promise.resolve(); } - + //Параметры прошли проверку, создаем экхемпляр контроллера прерывания асинхронного процесса const controller = new AbortController(); + //Подготовим объект для выбрасывания ошибки const sMessage = `Истёк интервал ожидания (${timeout} мс) завершения асинхронного процесса.`; const timeoutError = new Error(sMessage); timeoutError.error = sMessage; - + //Буфер для идентификатора таймера let timeoutPid; - + //Создаём промис с взведенным таумером на у казанный таймаут, промис будет "отклонён" по истечении таймера, но перед этим контроллер прерывания выдаст "сигнал" на отмену осинхронного процесса const timeoutPromise = new Promise((_, reject) => { timeoutPid = setTimeout(() => { controller.abort(timeoutError); reject(timeoutError); }, timeout); }); - + //Создаём промис с функцией-исполнителем асинхронного процесса const taskPromise = Promise.resolve(executor(controller.signal)); - + //Запускаем оба промиса в режиме "гонка" - кто "быстрее" исполнится return Promise.race([taskPromise, timeoutPromise]).finally(() => { + //В любом случае не забываем зачистить взведённый таймер if (timeoutPid) clearTimeout(timeoutPid); }); };