← Назад к вопросам

В чем разница между Promise и MutationObserver?

2.7 Senior🔥 91 комментариев
#JavaScript Core#Браузер и сетевые технологии

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Promise и MutationObserver

Promise и MutationObserver - это два совершенно разных инструмента, работающих в разных слоях JavaScript и браузера. Они решают разные задачи и имеют разные места в Event Loop.

Promise - для асинхронных операций

Promise используется для обработки асинхронного кода:

// Пример 1: Загрузка данных с сервера
const fetchUser = (id: string): Promise<User> => {
  return fetch(`/api/users/${id}`)
    .then(res => res.json());
};

fetchUser('123')
  .then(user => console.log('User loaded:', user))
  .catch(error => console.error('Failed to load:', error));

// Пример 2: Ожидание в коде
async function processData() {
  const data = await fetchData();
  console.log('Got data:', data);
}

Promise жизненный цикл:

const myPromise = new Promise((resolve, reject) => {
  // Pending state - выполняется сразу
  console.log('Promise created');
  
  setTimeout(() => {
    resolve('Done!');  // -> Fulfilled state
    // или reject('Error'); -> Rejected state
  }, 1000);
});

myPromise
  .then(result => console.log(result))    // 'Done!'
  .catch(error => console.error(error));

MutationObserver - для наблюдения за DOM

MutationObserver следит за изменениями DOM:

// Пример 1: Следить за всеми изменениями элемента
const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    console.log('DOM changed:', mutation.type);
    // mutation.type может быть:
    // - 'attributes'   - изменился атрибут (class, id, style)
    // - 'childList'    - добавлен/удален дочерний элемент
    // - 'characterData' - изменился текстовый контент
  });
});

const element = document.getElementById('myElement');
observer.observe(element, {
  attributes: true,      // Следить за атрибутами
  childList: true,       // Следить за добавлением/удалением дочерних элементов
  characterData: true,   // Следить за изменением текста
  subtree: true          // Следить и за потомками
});

// Остановить наблюдение
observer.disconnect();

Пример 2: Практическое применение

// Следить за появлением элемента
const observer = new MutationObserver((mutations) => {
  const newElement = document.querySelector('.dynamic-element');
  if (newElement) {
    console.log('New element appeared!');
    // Выполнить действие
    observer.disconnect(); // Перестать следить
  }
});

observer.observe(document.body, { childList: true, subtree: true });

Ключевые отличия

1. Назначение

// Promise - обработка асинхронных операций
Promise:
  - Загрузка данных
  - Таймауты
  - I/O операции (fetch, file reading)
  - Любая асинхронность

// MutationObserver - наблюдение за DOM
MutationObserver:
  - Следить за изменениями DOM
  - Реагировать на добавление/удаление элементов
  - Отслеживать изменения атрибутов
  - Работать с динамически создаваемым контентом

2. Когда выполняются в Event Loop

console.log('1. Start');

Promise.resolve()
  .then(() => console.log('2. Promise (microtask)'));

const observer = new MutationObserver(() => {
  console.log('3. MutationObserver (microtask)');
});

const element = document.createElement('div');
observer.observe(element, { childList: true });
element.appendChild(document.createElement('span'));

console.log('4. End');

// Порядок вывода:
// 1. Start
// 4. End
// 2. Promise (microtask)          <- Promise выполнится раньше
// 3. MutationObserver (microtask) <- MutationObserver выполнится после

// Почему? Обе - микротаски, но Promise имеет приоритет

3. Инициирование

// Promise - инициируется явно
const promise = new Promise((resolve) => {
  resolve('value');
});

// MutationObserver - автоматически срабатывает при изменении DOM
const observer = new MutationObserver(callback);
observer.observe(element, { childList: true });
// callback сработает при ANY изменении childList

4. Количество вызовов

// Promise - выполняется один раз
const promise = new Promise((resolve) => {
  resolve('value');
});

promise.then(() => console.log('Executed once'));
// Выведет ОДИН раз

// MutationObserver - может срабатывать много раз
const observer = new MutationObserver(() => {
  console.log('DOM changed');
});

observer.observe(document.body, { childList: true });

document.body.appendChild(document.createElement('div')); // Срабатывает
document.body.appendChild(document.createElement('div')); // Срабатывает снова
document.body.appendChild(document.createElement('div')); // Срабатывает еще
// Будет вызвана 3 раза

Использование на практике

Пример 1: Ждать загрузку элемента (MutationObserver)

function waitForElement(selector: string): Promise<Element> {
  return new Promise((resolve) => {
    const element = document.querySelector(selector);
    if (element) {
      resolve(element);
      return;
    }

    const observer = new MutationObserver(() => {
      const element = document.querySelector(selector);
      if (element) {
        resolve(element);
        observer.disconnect();
      }
    });

    observer.observe(document.body, { 
      childList: true, 
      subtree: true 
    });
  });
}

// Использование
await waitForElement('.dynamic-element');
console.log('Element found!');

Пример 2: Следить за изменениями класса (MutationObserver)

const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
      const element = mutation.target as HTMLElement;
      const classes = element.className;
      console.log('Classes changed to:', classes);
    }
  });
});

const button = document.querySelector('button')!;
observer.observe(button, { attributes: true });

button.classList.add('active'); // Срабатывает MutationObserver

Пример 3: Комбинирование Promise и MutationObserver

function waitForDataLoad(): Promise<void> {
  return new Promise((resolve) => {
    const observer = new MutationObserver((mutations) => {
      // Проверяем если данные загружены
      const dataElement = document.querySelector('[data-loaded]');
      if (dataElement) {
        observer.disconnect();
        resolve();
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  });
}

// Использование
await waitForDataLoad();
console.log('Data is loaded!');

Когда использовать что

// ИСПОЛЬЗУЙ PROMISE когда:
✓ Нужно дождаться асинхронной операции (fetch, setTimeout)
✓ Нужно обработать результат операции
✓ Нужна цепочка асинхронных операций
✓ Нужна обработка ошибок

const data = await fetch('/api/data').then(r => r.json());

// ИСПОЛЬЗУЙ MutationObserver когда:
✓ Нужно реагировать на изменения DOM
✓ Нужно следить за динамически добавляемым контентом
✓ Нужно найти элемент, который может появиться позже
✓ Нужно отслеживать изменения атрибутов

const observer = new MutationObserver(() => {
  console.log('DOM changed');
});
observer.observe(element, { attributes: true });

Важные моменты

// MutationObserver может быть heavy
// Каждое изменение вызывает callback
const observer = new MutationObserver(() => {
  performHeavyComputation(); // Может замедлить приложение!
});
observer.observe(document.body, { childList: true, subtree: true });

// Лучше добавить debounce
import { debounce } from 'lodash';

const observer = new MutationObserver(
  debounce(() => {
    performHeavyComputation();
  }, 300)
);
observer.observe(document.body, { childList: true, subtree: true });

Заключение

Promise - это инструмент для работы с асинхронностью, часть JavaScript. MutationObserver - это инструмент для наблюдения за DOM, браузерный API.

Они не конкурируют, а дополняют друг друга. Часто их используют вместе: Promise ждет результат, MutationObserver следит за изменениями DOM.