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); }); };