Всем привет.

Недавно я решил разобраться, как устроена внутренняя логика Promise в JavaScript, и как она описана в спецификации. Для этого я реализовал собственный Promise. В процессе стало понятно, что такое упражнение может быть полезно не только мне, поэтому я решил оформить свое исследование в виде статьи.

Статья рассчитана на разработчиков, которые уже используют Promise, но хотят понять, как они устроены внутри. Вы можете использовать этот текст как практическое руководство и пройти тот же путь вместе со мной. Лучший способ получить пользу от материала - писать код параллельно. Скорее всего, такой подход займёт несколько часов. Простое чтение в лучшем случае даст лишь поверхностное понимание.

В статье рассматривается упрощённая реализация Promise - ориентируемся на понимание базовой модели. Мы не стремимся полностью воспроизвести алгоритмы ECMAScript и сознательно откладываем работу с thenable-объектами и внутренними Job Records.

Источники

  • спецификация Promises/A+ - https://promisesaplus.com/

    Описывает базовую модель Promise, как абстрактный контракт поведения. Определяет, как должен работать then, как передаются значения и ошибки по цепочке. Не привязана к JavaScript и не описывает встроенные механизмы языка.

  • описание Promise в ECMAScript - https://tc39.es/ecma262/#sec-promise-objects

    Описывает встроенный Promise как часть языка JavaScript и задает конкретную реализацию: конструктор, executor, внутренние состояния и операции, правила асинхронного выполнения обработчиков.

  • MDN - https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise

    MDN не является нормативной спецификацией, но полезен для нас как справочник по публичному API и примерам использования.

Содержание

  1. Опишем общую структуру.

  2. Реализуем resolve и reject из конструктора.

  3. Реализуем then и встретимся с проблемами наивной реализации.

  4. Исправим проблемы отсутствия асинхронности в нашем коде.

  5. Добавим обработку, когда колбэк, переданный в then - не функция.

  6. Добавим обработку случая, когда колбэк, переданный в then возвращает промис.

  7. Добавим обработчик для колбэка onRejected.

  8. Подведем итоги: готовый конструктор и функция then.

Что известно о Promise

  1. Промис всегда находится в одном из трёх состояний: pending, fulfilled, rejected.

    https://promisesaplus.com/#promise-states

  2. В реализации Promise в ECMAScript executor-функция вызывается синхронно в момент создания объекта Promise.

    https://tc39.es/ecma262/#sec-promise-executor

  3. Значение value или причина ошибки reason сохраняются во внутреннем состоянии промиса и используются при вызове обработчиков, подписанных через then.

    https://tc39.es/ecma262/#sec-performpromisethen

  4. Метод then всегда возвращает новый Promise.

    https://promisesaplus.com/#point-40

Исходя из этого, начнём с общей структуры.

Общая структура Promise

class MyPromise {

    constructor(executor) {  /* Promise принимает executor-функцию */
        this.state = "pending";   /* начальное состояние */
        this.value = undefined;   /* значение для fulfilled */
        this.reason = undefined;  /* причина для rejected */

        const resolve = (value) => {}; /* реализуем дальше */
        const reject = (reason) => {}; /* реализуем дальше */

        executor(resolve, reject);
    }

    then(onFulfilled, onRejected) {}

    catch(onRejected) {}

    finally(onFinalized) {}
}

Реализация resolve и reject

Начнём с простой реализации resolve. Она должна:

  1. Перевести промис в состояние fulfilled.

  2. Сохранить результат.

const resolve = (value) => {
    this.state = "fulfilled";
    this.value = value;
};

Но после перехода в состояние fulfilled или rejected значение value или reason не должны изменяться. В реализации это выражается тем, что, если состояние не равно pending, то повторные вызовы resolve/reject игнорируются: https://promisesaplus.com/#point-21

Чтобы это реализовать, добавим проверку:

const resolve = (value) => {
    if (this.state !== "pending") {
      return;
    }
  
    this.state = "fulfilled";
    this.value = value;
};

Аналогично реализуем reject:

const reject = (reason) => {
    if (this.state !== "pending") {
      return;
    }
  
    this.state = "rejected";
    this.reason = reason;
};
Оговорка про состояние в нативных промисах

В нативной реализации Promise внутреннее состояние не является частью публичного API. Спецификация определяет состояния pending, fulfilled и rejected как внутренние слоты ([[PromiseState]]). Доступ к ним имеет только движок JavaScript: https://tc39.es/ecma262/#table-internal-slots-of-promise-instances .

В нашей реализации мы храним состояние в открытых свойствах объекта для наглядности и удобной отладки.

На этом этапе у нас есть базовый и корректный с точки зрения состояний Promise.

Проверка базовой логики

/* Promise с resolve */
const myPromiseResolve = new MyPromise((resolve, reject) => {
    resolve("hello promise");
});

console.log(myPromiseResolve.state); // fulfilled
console.log(myPromiseResolve.value); // hello promise

/* Promise с reject */
const myPromiseReject = new MyPromise((resolve, reject) => {
    reject("error in promise");
});

console.log(myPromiseReject.state);  // rejected
console.log(myPromiseReject.reason); // error in promise

Реализация then

По спецификации then:

Простейшая реализация может выглядеть так:

class MyPromise {
    /* ... констркутор итд */
    
    then(onFulfilled, onRejected) {
        /* then возвращает новый промис */
        return new MyPromise((resolve, reject) => {

            if (onFulfilled) {
                /* вызываем callback со значением исходного промиса */
                const result = onFulfilled(this.value); 
                resolve(result);
            }

            if (onRejected) {
              /* сделаем позже */
            }
        });
    }
}

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

Проблема №1: then выполняется синхронно

По спецификации обработчики onFulfilled и onRejected не должны вызываться синхронно. В Promises/A+ это сформулировано как требование, что обработчики могут быть вызваны только после того, как текущий стек выполнения будет полностью очищен: https://promisesaplus.com/#point-34

В ECMAScript описана реализация этого требования. Алгоритм PerformPromiseThen описывает, что обработчики не вызываются напрямую, а регистрируются как задания (jobs), которые попадают потом в очередь микрозадач: https://tc39.es/ecma262/#sec-performpromisethen

Проверим поведение нативного промиса:

new Promise(resolve => resolve(10)).then(console.log);
console.log("end");

Фактический вывод:

end
10

Попробуйте запустить то же самое в нашей реализации, и посмотреть, какой будет вывод.

Проблема №2: обработчики then могут выполняться раньше resolve

Проверим поведение нативного промиса:

new Promise(resolve => {
    setTimeout(() => {
        console.log("1 - вызываем resolve");
        resolve(100);
    }, 1000);
})
.then(value => console.log("2 - then получил", value));

Фактический вывод:

1 - вызываем resolve
2 - then получил 100

Попробуйте запустить это в нашей реализации, и посмотреть, какой будет вывод.

Вывод в нашей реализации
Спустя 1 секунду вывод:
then получил - undefined
1 - вызываем resolve

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

Попробуем исправить эти проблемы.

Асинхронный запуск обработчиков

Как мы выяснили выше, обработчики Promise onFulfilled и onRejected должны выполняться асинхронно: они добавляются в очередь и выполняются как микрозадачи. Реализуем это. Для простоты используем queueMicrotask: https://developer.mozilla.org/en-US/docs/Web/API/Window/queueMicrotask

Создадим вспомогательную функцию, которая будет помещать колбэки в очередь:

function runAsync(fn) { /* принимает на вход другую фунцию */
    queueMicrotask(fn); /* и кладет ее в очередь микро-задач */
}

Обновляем then

Для удобства добавим отдельную функцию-обработчик для колбэка onFulfilled.

class MyPromise {
    /* констркутор итд */

    then(onFulfilled, onRejected) { 
        return new MyPromise((resolve, reject) => {

            /* создадим функцию-обработчик для колбэка onFulfilled */
            const handleFulfilled = () => { 
                if (onFulfilled) {
                    const result = onFulfilled(this.value);
                    resolve(result);
                }
            }
            
            /* далее решаем, что делать с этой функцией
            в зависимости от состояния промиса */
            
            if (this.state === "fulfilled") { // если промис выполнен успешно
                // то вызываем обработчик колбэка, но асинхронно
                runAsync(handleFulfilled); 
            }
            
            if (this.state === "pending") {
                /* ЧТО ДЕЛАТЬ ЗДЕСЬ? */
            }
            
            if (this.state === "rejected") {
                /* напишем позже, когда напишем обработчик
                для колбэка onRejected  */
            }


            if (onRejected) {/* сделаем позже*/}
        });
    }
}

Теперь доработаем конструктор.

Хранилища обработчиков в конструкторе

Когда Promise находится в состоянии pending, мы не можем выполнить обработчики, поэтому их надо где-то хранить. Для этого создадим два массива: fulfilledHandlers и rejectHandlers. А при вызове resolve и reject выполним все накопленные обработчики в этих массивах асинхронно. Под обработчиками здесь мы понимаем функции, которые будут вызваны при переходе промиса в fulfilled или rejected.

Оговорка про разделение fulfilledHandlers и rejectHandlers

В нативной реализации Promise обработчики, переданные в then, не хранятся в виде отдельных списков для успешного и ошибочного завершения. В нашей реализации мы сознательно используем два отдельных массива fulfilledHandlers и rejectHandlers, чтобы сделать код более наглядным, и отдельно показать обработку успешного и ошибочного завершения промиса.

constructor(executor) { 
    this.state = "pending"; 
    this.value = undefined; 
    this.reason = undefined; 
  
    /* добавим массивы для хранения обработчиков fulfilled и reject */
    this.fulfilledHandlers = []; 
    this.rejectHandlers = [];
    
    const resolve = (value) => {
        if (this.state !== "pending") return;
        this.state = "fulfilled";
        this.value = value;
        /* выполняем асихронно все колбэки */
        runAsync(() => this.fulfilledHandlers.forEach(callback => callback())); 
    }
    
    const reject = (reason) => { 
        if (this.state !== "pending") return;
        this.state = "rejected";
        this.reason = reason;
          /* выполняем асихронно все колбэки */
        runAsync(() => this.rejectHandlers.forEach(callback => callback()));
    }
    
    /* сделаем более безопасным вызов executor - функции */
    try {
       executor(resolve, reject);
    } catch(e) {
      reject(e);
    }
}

Теперь then может просто сохранять обработчик:

if (this.state === "pending") {
    this.fulfilledHandlers.push(handleFulfilled);
}

Проверка асинхронности и цепочек

new MyPromise((resolve) => {
    console.log("promise start");
    
    setTimeout(() => {
        console.log("1 - вызываем resolve");
        resolve(1);
    }, 1000);
})
    .then(value => {
        console.log("then 1");
        console.log(value);
        return value + 1;
    })
    .then(value => {
        console.log("then 2");
        console.log(value);
    });

console.log("sync");
Вывод в консоль
promise start
sync
1 - вызываем resolve
then 1
1
then 2
2

Мы устранили две проблемы, о которых писали выше, теперь поведение соответствует нативному Promise. Попробуйте самостоятельно придумать и протестировать любую другую цепочку.

Рассмотрим еще два популярных случая, связанных с колбэком onFulfilled.

№1: когда onFulfilled - не функция

В спецификации указано, если onFulfilled не функция, то обработчик игнорируется, а исходное значение пробрасывается дальше без изменений: https://promisesaplus.com/#point-23.

const handleFulfilled = () => {
    /*  добавим проверку, функция ли пришла в качестве колбэка */
    if (typeof onFulfilled !== "function") {
        /* и если нет, то просто зарезолвим value */
        resolve(this.value); 
        return;
    }   

   /* заодно сделаем вызов более безопасным */
    try { 
        const result = onFulfilled(this.value);
        resolve(result);                    
    } catch (e) {
        reject(e);
    }
}
new MyPromise((resolve, reject) => resolve(1))
    /* передана не функция, 1 пробрасывается дальше без изменений */
    .then(2) 
     .then(value => {
        console.log("then");
        console.log(value); // вывод 1
    });

№2: когда onFulfilled возвращает другой Promise

Рассмотрим цепочку:

getUser()
    .then(user => getOrders(user.id)) // возвращается Promise
    .then(orders => console.log(orders));

Здесь функция, переданная в первый then, возвращает Promise. В этом случае цепочка должна работать так:

  • следующий then ждёт, пока этот Promise завершится;

  • он должен получить не сам Promise, а его результат;

  • если вложенный Promise завершился с ошибкой, ошибка передаётся дальше по цепочке.

Если этого не сделать, цепочки промисов не будут работать.

Уточнение о разрешении значения Promise

Значение промиса не может быть другим промисом или thenable-объектом, и должно быть разрешено до конечного значения. В статье мы отклоняемся от нативного поведения и не реализуем эту логику в resolve в конструкторе. Обрабатываем Promise только на уровне then.

В спецификации Promise/A+ указано: если onFulfilled или onRejected возвращает Promise, то Promise, возвращаемый методом then, не должен завершаться сразу. Он обязан принять состояние возвращённого Promise и завершиться тем же образом: https://promisesaplus.com/#point-44

Другими словами:

  • fulfilled Promise превращает цепочку в fulfilled с тем же значением;

  • rejected Promise превращает цепочку в rejected с той же причиной;

  • до этого момента выполнение цепочки приостанавливается.

Такое поведение обеспечивает корректную работу цепочек then.

Текущий then

Напомним, как теперь выглядит наш then:

then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {

        const handleFulfilled = () => {
            if (typeof onFulfilled !== "function") {
                resolve(this.value);
                return;
            }

            try {
                const result = onFulfilled(this.value);
                if (result instanceof MyPromise) {
                    /* что делать в этом случае? */
                   
                } else {
                    resolve(result);
                }
            } catch (e) {
                reject(e);
            }
        };

        if (this.state === "fulfilled") {
            runAsync(handleFulfilled);
        }

        if (this.state === "pending") {
            this.fulfilledHandlers.push(handleFulfilled);
        }

        if (this.state === "rejected") {
            /* обработаем позже */
        }
    });
}
Оговорка про использование instanceof

В реальной спецификации Promise/A+ проверка устроена по-другому. Спецификация не проверяет принадлежность к классу Promise. Вместо этого используется процедура разрешения промиса, работающая с thenable-объектами - любыми объектами или функциями, у которых есть метод then: https://promisesaplus.com/#the-promise-resolution-procedure.

В нашей статье мы сознательно ограничиваемся проверкой instanceof MyPromise.

Если then вернул Promise

Если result - это Promise, то мы просто вызываем then на нем:

result.then(resolve, reject);
  • когда result выполнится - мы вызываем resolve(value) нового Promise;

  • если result упадёт - вызываем reject(reason) нового Promise.

То есть по сути мы подписываемся на результат другого Promise и передаём его состояние дальше.

Обновим код:

const result = onFulfilled(this.value);

if (result instanceof MyPromise) {
    result.then(resolve, reject);
    return;
}

resolve(result);

Проверим

const p = new MyPromise((resolve) => {
    resolve(1);
});

/* первый then возвращает другой Promise */
p.then(value => {
    return new MyPromise((resolve) => {
        setTimeout(() => {
            resolve(value + 1);
        }, 100);
    });
})
/* второй then ждёт, пока этот Promise завершится */
  .then(result => {
    /* и получает значение, а не сам объект Promise */
    console.log(result); // ожидаем 2
});

Мы обработали сложный и популярный кейс в промисах.

Реализация обработки onRejected

Теперь добавим обработчик ошибок. Он реализуется практически также, как и handleFulfilled.

  • провряем, является ли колбэк функцией;

  • запускаем колбэк, потом проверяем, не является ли результат промисом;

  • вызываем resolve с полученным результатом.

then(onFulfilled, onRejected) {
    /* .... другой код*/
  
    const handleReject = () => {
        if (typeof onRejected !== "function") {
            reject(this.reason);
            return;
        }

        try {
            const result = onRejected(this.reason);
            if (result instanceof MyPromise) {
                result.then(resolve, reject);
                return;
            }

            // важно, именно resolve
            resolve(result);


        } catch (e) {
            reject(e);
        }
    }
}

И подключаем его:

if (this.state === "rejected") {
    runAsync(handleReject);
}

if (this.state === "pending") {
    this.fulfilledHandlers.push(handleFulfilled); // уже было
    this.rejectHandlers.push(handleReject);
}

Почему resolve, а не reject?

Важно обратить внимание, что когда onRejected успешно обработал ошибку и не выбросил исключение, то цепочка должна стать fulfilled. Для этого нужно вызвать resolve с полученным result.

Пример нативного поведения:

Promise.reject("error")
    .then(null, err => {
        console.log("handled:", err);
        return "ok";
    })
    .then(console.log);

Вывод:

handled: error
ok

Ошибка обработана, выполнение продолжается нормально. Посмотрим, как это работает в нашем случае.

onRejected возвращает Promise (fulfilled):

new MyPromise((resolve, reject) => {
    reject("error");
})
    .then(null, (err) => {
        /* фиксим ошибку и возвращаем Promise */
        return new MyPromise((resolve) => {
            resolve("fixed");
        });
    })
    .then(value => console.log("OK:", value)); // ожидаем OK fixed;

onRejected возвращает Promise (rejected):

new MyPromise((resolve, reject) => {
    reject("error");
})
    .then(null, (err) => {
        /* обработчик тоже возвращает Promise, но он rejected */
        return new MyPromise((resolve, reject) => {
            reject("not fixed");
        });
    })
    .then(
        value => console.log("OK:", value),
        err => console.log("ERR:", err) // ожидаем: ERR: not fixed
    );

Финальная версия промиса

function runAsync(fn) {
    queueMicrotask(fn);
}

class MyPromise {

    constructor(executor) {
        this.state = "pending";
        this.value = undefined;
        this.reason = undefined;
        this.fulfilledHandlers = [];
        this.rejectHandlers = [];

        const resolve = (value) => {
            if (this.state !== "pending") return;
            this.state = "fulfilled";
            this.value = value;
            runAsync(() => this.fulfilledHandlers.forEach(callback => callback()));
        }

        const reject = (reason) => {
            if (this.state !== "pending") return;
            this.state = "rejected";
            this.reason = reason;
            runAsync(() => this.rejectHandlers.forEach(callback => callback()));
        }


        try {
            executor(resolve, reject);
        } catch(e) {
            reject(e);
        }
    }


    then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {

            const handleFulfilled = () => {
                if (typeof onFulfilled !== "function") {
                    resolve(this.value);
                    return;
                }

                try {
                    const result = onFulfilled(this.value);

                    if (result instanceof MyPromise) {
                        result.then(resolve, reject);
                        return;
                    }
                    resolve(result);

                } catch (e) {
                    reject(e);
                }
            }

            const handleReject = () => {
                if (typeof onRejected !== "function") {
                    reject(this.reason);
                    return;
                }

                try {
                    const result = onRejected(this.reason);
                    if (result instanceof MyPromise) {
                        result.then(resolve, reject);
                        return;
                    }
                    resolve(result);

                } catch(e) {
                    reject(e);
                }
            }

            if (this.state === "fulfilled") {
                runAsync(handleFulfilled);
            }

            if (this.state === "pending") {
                this.fulfilledHandlers.push(handleFulfilled);
                this.rejectHandlers.push(handleReject);
            }

            if (this.state === "rejected") {
                runAsync(handleReject);
            }
        });
    }

    catch(onRejected) {
        // TODO
    }

    finally(onFinally) {
        // TODO
    }
    
}

Итоги

У нас есть собственная реализация Promise, которая повторяет базовую модель работы нативных промисов:

  • асинхронное выполнение обработчиков;

  • цепочки then;

  • корректную передачу значений и ошибок;

  • поддержку вложенных Promise.

Мы сознательно упростили ряд моментов: не реализовывали работу с thenable-объектами в общем виде, не рассматривали внутренние Job Records и не стремились полностью воспроизвести алгоритмы ECMAScript. Цель была разобраться в базовой логике.

Надеюсь, что вы дошли до этого места и писали код параллельно, и теперь воспринимаете Promise как вполне конкретный механизм с понятными правилами.

Что можно сделать самостоятельно

  1. Написать тесты для проверки цепочек.

  2. Реализовать catch .

  3. Реализовать finally.

  4. Попробовать заменить instanceof MyPromise на более универсальную проверку thenable-объектов.

Ссылка на полную реализацию промиса c готовыми catch и finally: https://codepen.io/zheleznikov/pen/dPMjmBM

Комментарии (14)


  1. yarkov
    04.02.2026 15:45

    У нас есть собственная реализация Promise, которая повторяет базовую модель работы нативных промисов

    Осталось понять для чего нам это и будет счастье ))


    1. polwen Автор
      04.02.2026 15:45

      :)

      Это описание реализации, чтобы разобраться как устроен промис через попытку создать его поведение. В таком виде он еще сыроват для использования.


  1. Beholder
    04.02.2026 15:45

    теперь поведение соответствует нативному Promise

    Это довольно сомнительно, так как нативные микротаски обрабатываются раньше нативных промисов. Получилась разве что "игрушечная" имитация для учебных целей. Вряд ли можно написать полноценную замену по спецификации если нет нативной поддержки.


    1. polwen Автор
      04.02.2026 15:45

      Согласен с вами.
      Я пишу в статье не полноценную замену нативного Promise, а скорее воспроизвожу его модель поведения. Цель была скорее разобраться и показать, как именно работает промис через попытку его повторить.


    1. Alexandroppolus
      04.02.2026 15:45

       нативные микротаски обрабатываются раньше нативных промисов

      Не совсем понял этот поинт. Вот здесь будет вывод 0 1 2, и в ноде, и в браузере:

      queueMicrotask(() => console.log(0))
      Promise.resolve().then(() => console.log(1));
      queueMicrotask(() => console.log(2))

      второй queueMicrotask не вылез вперед then


      1. InveterateCoder
        04.02.2026 15:45

        Потому что Промисы тоже микротаски)


    1. alexchizik
      04.02.2026 15:45

      И всё-таки это модельно корректная реализация, которая нужна для общего понимания как это работает "под капотом". Это примерно то же самое, что надо бы, что бы разработчики знали как работают формы в браузере, применять на практике это скорее всего не придётся, но кто знает)


    1. InveterateCoder
      04.02.2026 15:45

      Это скорее некорректно. Нативные промисы и есть микротаски, и написать свои промисы вполне возможно используя queueMicrotask(), и делается довольно часто, если поискать cancelable promises, в npm репе.


  1. Alexandroppolus
    04.02.2026 15:45

    Проверку instanceof MyPromise (точнее, проверку на thenable, о чем упоминается в конце статьи) так же надо делать в конструкторе промиса, в функции resolve


    1. polwen Автор
      04.02.2026 15:45

      Спасибо, что обратили на это внимание.

      Я упростил этот момент и обрабатываю thenable только в then , что не смешивать. Главное было найти баланс между тем, чтобы было похоже на нативный промис и при этом не переусложнить описание.


      1. Alexandroppolus
        04.02.2026 15:45

        Я упростил этот момент и обрабатываю thenable только в then , что не смешивать

        Ну тут вы радикально упростили ) Значением промиса не может быть другой промис (thenable), это ключевой момент, оно резолвится "до упора".

        Логику resolve даже надо не добавить, а перенести в конструктор, тогда она не понадобится внутри then


        1. polwen Автор
          04.02.2026 15:45

          Согласен. Спасибо, что подсветили этот момент. Потому что мне бы не хотелось, чтобы были грубые нарушения. Переписывать реализацию - это наверное чересчур. Поэтому добавил в текст оговорку на эту тему.


  1. winkyBrain
    04.02.2026 15:45

    Подход "написать своё, чтобы понять, как работает уже существующее" - очень классный и рабочий! Когда учился, на одном из курсов по React на ютубе, прежде чем переходить к использованию Redux(само собой после объяснений, для чего вообще нужен глобальный стор), автор предложил вместе написать свой. И этот шаг позволил начинающим изучать впоследствии настоящий Redux с хотя бы примерным пониманием того, как оно в целом там всё устроено. За это лайк)

    А вот от строковых литералов повсюду, особенно ключевых состояний промиса, кровь из глаз. Енамы нам не завезли, так как тут вроде голый JS, но можно же было например словарь какой-нибудь слепить или просто три строковых константы сделать в крайнем случае, и к ним уже везде обращаться.


    1. polwen Автор
      04.02.2026 15:45

      Рад, что такой подход вам близок :)

      По поводу строковых литералов, согласен. Можно было бы сделать объект для хранения значений, но я решил так не делать, чтобы примеры в тексте читались проще. И читателю не нужно было держать в голове дополнительные переменные, которые не видны в самом фрагменте.