На днях я просматривал работу младшего разработчика и заметил, что код извлекает автора истории один раз для каждой истории в списке. Полезная нагрузка истории содержала ключ автора с URL-адресом сведений об этом авторе примерно так:

{
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "url": "http://localhost:3000/api/stories/2/",
            "title": "Emoji Movie",
            "tagline": "Oh Boy! what a pile of 💩",
            "author": "http://localhost:3000/api/authors/1/",
            "html": "...",
            "published_at": "2019-02-19T00:17:53.821513Z",
            "can_edit": false
        },
        {
            "url": "http://localhost:3000/api/stories/1/",
            "title": "Mercy Killing",
            "tagline": "Sometimes you just have to let go",
            "author": "http://localhost:3000/api/authors/1/",
            "html": "<p>Today was the day she would be saying ...",
            "next_chapter": null,
            "can_edit": false
        }
    ]
}

Вкладка «Сеть» подтвердила, что она дважды извлекает данные об авторе через API, по одному разу каждый раз, когда этот автор находится в списке последних историй.

Когда я сказал, что нам нужно кэшировать эти результаты, он спросил, как мы это делаем. Я попытался объяснить концепцию через WhatsApp (мы работаем удаленно), но ему было трудно понять ее, поэтому я собрал простой пример того, как сделать это кеширование. Вот как выглядел его код до кэширования.

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

Чтобы понять, как AuthorCache творит чудеса и предотвращает дополнительные запросы, нам нужно понимать, что у любого объекта автора есть как минимум 4 состояния, прежде чем мы сможем правильно решить эту проблему.

  1. Нет в кеше и никогда не запрашивался.
  2. Не в кеше, а по пути с сервера (по запросу, до ответа).
  3. В кеше и свежий/текущий
  4. В кеше и устарело

В этой статье я расскажу только о первых 3-х пунктах, №4 на самом деле довольно сложная проблема, которая во многом зависит от решаемой проблемы и количества клиентов, работающих над ней одновременно.

Ниже приведен код для AuthorCache, если данных вообще нет в кеше, мы их извлекаем (используя код из оригинальной реализации). и сохранить в кеше

  1. Мы обрабатываем первый случай, выполняя выборку (строки 22–31). И как только у нас есть обещание из выборки, мы сохраняем обещание в кеше, используя URL-адрес в качестве ключа. Когда обещание завершается, мы заменяем обещание фактическими данными, чтобы не запирать обещание навсегда в памяти. Мы возвращаем обещание, чтобы вызывающий мог знать, когда данные получены. Если бы мы просто сохранили промис в кеше, никогда не заменяя его фактическими данными, он все равно работал бы, НО это содержало бы XHR (привязанный к промису) и несколько других объектов, которые нам больше не нужны. Заменяя обещание объектом, мы позволяем освободить XHR и обещание вокруг него в какой-то момент в будущем.
  2. Во втором случае (строки 8–11) мы возвращаем исходное обещание, поскольку запрос уже находится в процессе выполнения, и мы хотим дождаться его завершения. В этом разделе есть небольшая проблема, заключающаяся в том, что объекты, которые мы кэшируем, не могут иметь свойство с именем then. если результат обещан.
  3. В последнем случае (строки 13–19) мы должны создать промис и разрешить его, чтобы мы могли вернуть данные, которые уже есть в кеше. Возвращая обработанное обещание напрямую, мы решаем проблему сохранения единообразия интерфейса, getAuthor всегда возвращает обещание, поэтому код, использующий кеш, никогда не должен знать или заботиться о том, являются ли данные локальными, удаленными или в полете.

Гнарлийская проблема устаревших записей в кеше связана с аннулированием кеша и должна учитывать несколько вещей. Хотим ли мы кэшировать только N авторов, как долго мы можем кэшировать автора, прежде чем его данные устареют? Как обновить автора, если компонент не ищет новые данные? (Подсказка: использование избыточности и сохранение авторов в состоянии избыточности). А если на сервере поменяется автор? Если мы подписались на обновления для этого автора, мы должны иметь дело с изменениями, отправленными с сервера. Все это может быть более серьезной проблемой, чем та, которую мы здесь решили.