Я хочу сделать запрос и кешировать его в функциональном стиле.
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');Два вопроса:
Этот код идиоматичен?
Как я могу предоставить логику для генерации ключа кеша на основе результата, сохраняя при этом разделение проблем? Например, я мог бы использовать req для получения различных видов ресурсов, которым нужна разная логика для генерации ключа, который будет использоваться в кеше. Как мне передать эту логику генерации ключей функции cache?
Редактировать: На самом деле ключом должен быть URI (спасибо @epascarello). Я выбрал плохой пример. Но я хотел бы спросить о более общем случае, когда логика должна быть представлена «компоновкой вниз», сохраняя при этом достойное разделение проблем.
установите npmjs.com/package/object-hash, затем используйте var key = hash (args)
Ваша функция cache на самом деле ничего не кеширует. Не могли бы вы предоставить свой настоящий код?
Кэширование требует изменяемого состояния, которое в любом случае требует особой обработки в функциональном программировании, поэтому здесь нет ничего, что было бы «идиоматическим функциональным стилем».
Это надуманный пример. К сожалению, настоящего кода нет. Предположим, что этап кеширования обращается к экземпляру Redis с помощью set.
Ленивая оценка позволяет создавать бесконечные структуры данных с присущим им поведением кэширования. Подробнее Детали.
"Этот код идиоматичен?" - если вы ссылаетесь на часть const cachedReq = cache(req); cachedReq('example.com/foo');, а не на реализацию, да, эта идея полностью подходит. «Как мне передать эту логику генерации ключей функции кеширования?» - как второй (возможно, необязательный) параметр для cache(…).
Спасибо. Деталь const cache = (fn) => (...args) => fn(...args).... Это тоже идиоматика? т.е. построение функции для работы с композицией. Наконец, что касается «функционального стиля», следует ли использовать другой термин? Возможно, «экспрессионно-ориентированный стиль». Я не имею в виду, что использую здесь настоящее функциональное программирование, но я делаю различие между кодом, ориентированным на инструкции в стиле C.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы почти достигли своей цели, вы в правильном направлении, с концепцией композиции. возможно, этот код поможет вам в достижении вашей цели.
Давайте смоделируем вашу функцию 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 секунды и кеширует ответ, чтобы использовать его в следующий раз.
Надеюсь, это поможет тебе.
Спасибо. Мой вопрос действительно о том, как передать функциональность по «цепочке» композиции. Возможно, единственный ответ: отправьте его в исходном аргументе.
Вы можете использовать комбинацию 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(...);
Из этого выпадают некоторые приятные свойства:
Использовать uri в качестве ключа?