Использование композиции в JavaScript

Я хочу сделать запрос и кешировать его в функциональном стиле.

const req = (uri) => 
    (console.info(`requesting: ${uri}`), Promise.resolve({ status: 200 }));

const cache = (fn) => (...args) => 
    fn(...args).then((result) => { console.info('caching:', result) });

const cachedReq = cache(req);

cachedReq('example.com/foo');

Два вопроса:

  1. Этот код идиоматичен?

  2. Как я могу предоставить логику для генерации ключа кеша на основе результата, сохраняя при этом разделение проблем? Например, я мог бы использовать req для получения различных видов ресурсов, которым нужна разная логика для генерации ключа, который будет использоваться в кеше. Как мне передать эту логику генерации ключей функции cache?

Редактировать: На самом деле ключом должен быть URI (спасибо @epascarello). Я выбрал плохой пример. Но я хотел бы спросить о более общем случае, когда логика должна быть представлена ​​«компоновкой вниз», сохраняя при этом достойное разделение проблем.

Использовать uri в качестве ключа?

epascarello 10.01.2019 14:28

установите npmjs.com/package/object-hash, затем используйте var key = hash (args)

Sergej 10.01.2019 14:29

Ваша функция cache на самом деле ничего не кеширует. Не могли бы вы предоставить свой настоящий код?

Bergi 10.01.2019 14:35

Кэширование требует изменяемого состояния, которое в любом случае требует особой обработки в функциональном программировании, поэтому здесь нет ничего, что было бы «идиоматическим функциональным стилем».

Bergi 10.01.2019 14:36

Это надуманный пример. К сожалению, настоящего кода нет. Предположим, что этап кеширования обращается к экземпляру Redis с помощью set.

Ben Aston 10.01.2019 14:44

Ленивая оценка позволяет создавать бесконечные структуры данных с присущим им поведением кэширования. Подробнее Детали.

user10675354 10.01.2019 14:53

"Этот код идиоматичен?" - если вы ссылаетесь на часть const cachedReq = cache(req); cachedReq('example.com/foo');, а не на реализацию, да, эта идея полностью подходит. «Как мне передать эту логику генерации ключей функции кеширования?» - как второй (возможно, необязательный) параметр для cache(…).

Bergi 10.01.2019 15:04

Спасибо. Деталь const cache = (fn) => (...args) => fn(...args).... Это тоже идиоматика? т.е. построение функции для работы с композицией. Наконец, что касается «функционального стиля», следует ли использовать другой термин? Возможно, «экспрессионно-ориентированный стиль». Я не имею в виду, что использую здесь настоящее функциональное программирование, но я делаю различие между кодом, ориентированным на инструкции в стиле C.

Ben Aston 10.01.2019 15:12
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
8
131
2

Ответы 2

Вы почти достигли своей цели, вы в правильном направлении, с концепцией композиции. возможно, этот код поможет вам в достижении вашей цели.

Давайте смоделируем вашу функцию req следующим образом:

var req = (uri) => {
    console.info("inside req", uri);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({ status: 200 });
        }, 3000);
    });
}

тогда у вас есть версия cacheFunc как:

var withCache = (promiseFunc) => {
    const cache = {};
    return (...args) => {
        // suppose first param is uri
        var uri = args[0];
        return new Promise((resolve, reject) => {
            if (cache.hasOwnProperty(uri)) {
                return resolve(cache[uri]);
            }
            promiseFunc(...args).then((data) => {
              cache[uri] = data;
              resolve(data);
            }).catch(reject);
        });
    }
}

как видите, вам нужно создать объект cache в первой функции, так что это немного похоже на Каррирование в JS, поэтому вам нужно обернуть ваше req (то есть обещание) в другое обещание из версии кеша, поэтому перед выполните функцию req, вам необходимо проверить, существует ли какой-либо ответ в кеше с тем же ключом uri, если он есть, поэтому немедленно разрешите обещание, иначе выполните функцию req, как только вы получите ответ кеш-ответ и разрешите кеш-обещание версия.

Таким образом, вы можете использовать это так:

var cacheReq = withCache(req);
cacheReq('https://anywhere.com').then(console.info.bind(null, 'response')).catch(console.info.bind(null, 'error response'));

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

Надеюсь, это поможет тебе.

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

Ben Aston 10.01.2019 15:04

Вы можете использовать комбинацию Map и Конструктор запросов:

// I'll be using ramda for object equality, but any
// deepEquals checker should work.
const R = window.R;

const genRequest = ((cache, eqComparator) => {
  return (url, fetchOpts = {}) => {
    const key = {url, fetchOpts};
    const alreadyHave = [...cache.keys].find(x => eqComparator(x, key));
    if (alreadyHave) return cache.get(alreadyHave);
    const req = new Request(url, fetchOpts);
    cache.set(key, req);
    return req;
  };
})(new Map(), R.equals);

const req = genRequest('http://www.google.com');
fetch(req)
  .then(...)
  .catch(...);

Из этого выпадают некоторые приятные свойства:

  1. Каждый запрос создается только один раз, но может повторяться повторно.
  2. Никаких побочных эффектов до тех пор, пока вы не получите: создание запроса и его получение - отдельные части.
  3. ... таким образом, проблемы настолько разделены, насколько это возможно.
  4. Вы можете повторно настроить приложение параметров jigger, чтобы легко поддерживать настраиваемые сравнения на равенство с использованием одного и того же кеша.
  5. Вы можете использовать ту же стратегию для кэширования Результаты выборки отдельно от кеширования запросов.

Другие вопросы по теме