Мемоизация

Мемоизация — это техника оптимизации вычислений, при которой результаты функций сохраняются для последующего повторного использования. В контексте FAST Element, мемоизация особенно полезна при работе с шаблонами, рендерами и вычисляемыми свойствами, где частые пересчёты могут негативно влиять на производительность.


Вычисляемые свойства и мемоизация

FAST Element позволяет создавать вычисляемые свойства через геттеры, аннотированные @observable и @computed. Без мемоизации каждый доступ к такому свойству приводит к пересчёту, что может быть дорого при сложных вычислениях.

Пример вычисляемого свойства без мемоизации:

import { FASTElement, html, observable } from "@microsoft/fast-element";

export class PriceCalculator extends FASTElement {
  @observable basePrice = 100;
  @observable taxRate = 0.2;

  get totalPrice() {
    console.log("Пересчет totalPrice");
    return this.basePrice * (1 + this.taxRate);
  }
}

Каждый доступ к totalPrice вызывает пересчёт, даже если значения basePrice и taxRate не изменились. В больших приложениях это приводит к ненужной нагрузке.


Реализация мемоизации вручную

Самый простой способ мемоизации — кэшировать результат и сбрасывать его при изменении зависимостей:

export class PriceCalculator extends FASTElement {
  @observable basePrice = 100;
  @observable taxRate = 0.2;

  _cachedTotal = null;
  _lastBase = null;
  _lastTax = null;

  get totalPrice() {
    if (this.basePrice !== this._lastBase || this.taxRate !== this._lastTax) {
      this._lastBase = this.basePrice;
      this._lastTax = this.taxRate;
      this._cachedTotal = this.basePrice * (1 + this.taxRate);
      console.log("Пересчет totalPrice");
    }
    return this._cachedTotal;
  }
}

Ключевые моменты:

  • Храним предыдущие значения зависимостей (_lastBase, _lastTax).
  • Считаем заново только при изменении хотя бы одного из них.
  • Кэшируем результат для повторного использования (_cachedTotal).

Мемоизация в шаблонах

В FAST Element шаблоны тоже можно оптимизировать через мемоизацию вычисляемых выражений. Для этого используется функция memoize или кастомная функция-помощник.

Пример использования мемоизации для сложного выражения в шаблоне:

import { html, FASTElement, observable } from "@microsoft/fast-element";

function memoize(fn) {
  let lastArgs = null;
  let lastResult = null;
  return function(...args) {
    if (!lastArgs || !args.every((arg, i) => arg === lastArgs[i])) {
      lastArgs = args;
      lastResult = fn(...args);
    }
    return lastResult;
  };
}

export class UserList extends FASTElement {
  @observable users = [];

  get activeUsersCount() {
    return this._countActiveUsers(this.users);
  }

  _countActiveUsers = memoize(users => {
    console.log("Пересчет активных пользователей");
    return users.filter(u => u.active).length;
  });
}

UserList.template = html`
  <div>Активных пользователей: ${x => x.activeUsersCount}</div>
`;

Особенности:

  • memoize сохраняет результат функции при неизменных аргументах.
  • Повторный доступ к activeUsersCount не вызывает пересчёт, если массив users не изменился.
  • Легко адаптировать для других вычисляемых значений.

Связь с реактивностью

Мемоизация особенно эффективна вместе с реактивной системой FAST Element:

  • @observable отслеживает изменения данных.
  • Вычисляемые свойства и мемоизация уменьшают количество лишних рендеров.
  • Кэширование предотвращает дорогостоящие вычисления при каждом обновлении DOM.

Практические рекомендации

  1. Мемоизировать следует только дорогие вычисления, иначе накладные расходы на кэширование могут превысить выгоду.
  2. Использовать мемоизацию зависимостей, а не просто результатов, чтобы избежать устаревших данных.
  3. Для массивов и объектов учитывать, что сравнение ссылок работает быстрее, чем глубокое сравнение значений.
  4. При работе с шаблонами мемоизация помогает снизить количество пересозданий DOM-узлов, улучшая отзывчивость интерфейса.

Продвинутые техники

  • Композиция мемоизированных функций: объединение нескольких мемоизированных вычислений позволяет строить цепочки реактивных зависимостей без лишних пересчётов.
  • Мемоизация с ограничением размера кэша (LRU): полезно для больших наборов данных, чтобы кэш не разрастался без контроля.
  • Мемоизация и асинхронные операции: можно кэшировать промисы, избегая повторных сетевых запросов или тяжёлых асинхронных вычислений.

Мемоизация в FAST Element — инструмент, позволяющий максимально эффективно использовать реактивные свойства и шаблоны, снижая нагрузку на рендеринг и повышая производительность приложений.