Как правильно войти в Sanctuary / Fluture?
Фон
У меня есть функция, которая называется logInfoAsync
, Давайте рассмотрим, как эта функция отправляет некоторую информацию на сервер журналов по сети. Для целей этого вопроса предположим, что функция реализована следующим образом:
const logInfoAsync = logger => message =>
new Promise( ( resolve, reject ) => {
setTimeout( () => {
//random fail reason.
if( message.length > 10 ){
reject( "We have angered the Gods. Message is too big!" );
return;
}
logger(message);
resolve();
}, 2000 );
})
Использование:
const myLog= logInfoAsync( console.log );
myLog("Hello world!") // Rejected: "We have angered the Gods. Message is too big!"
myLog("Hello") // Resolved: "Hello"
проблема
Все идет нормально. У нас есть стандартный логгер, который иногда работает, а иногда злит Богов.
Теперь давайте предположим, что у нас есть последовательная серия async
расчеты:
const { Future } = require("Fluture");
const myLogF= Future.encaseP( logInfoAsync( console.log ) );
//request.get returns a Future and so does saveDB
request.get("http://mywebsite.com")
.chain( response => myLogF(`res is: ${res}`) )
.chain( saveDB )
.chain( ( ) => myLogF("Saved!") )
.fork(
err => console.log(`Something failed badly!: ${err}`),
( ) => console.log("Data saved to Olympus with great success!")
);
В этом примере, что произойдет, если лесоруб разозлит богов? Что ж, нам не удалось сохранить данные! Возможно, запрос прошел нормально, и, возможно, данные были действительны, но из-за сбоя регистратора мы облажались!
Исследование
Теперь возможное решение этого было бы использовать Fluture.bimap
после каждого журнала.Это было бы ужасно.
Я не хочу, чтобы мои журналы были более инвазивными, чем они есть, и я совершенно определенно не хочу, чтобы засорять мой код в стиле Promise try/catch
s.
К сожалению для меня, это единственное, о чем я могу подумать... Я думаю, что лучшим вариантом будет, например, резервный логгер, console.error
который используется, если myLogF
потерпеть неудачу, но я в идеале хотел бы, чтобы это было невидимым.
Приложение не должно знать, что оно регистрируется вообще!
Вопросы
Итак, учитывая этот фрагмент, у меня есть следующие вопросы:
- Как бы вы продолжали цепочку, если журнал не работает?
- Как бы вы сделали сбой журнала и восстановление невидимым для приложения (не засоряя его эквивалентом (
try/catch
)? - Какой шаблон наиболее часто используется для журналов?
1 ответ
Я бы сделал tap
функция специально для фьючерсов, например:
const always = x => y => x
const tapF = f => x => f(x).fold(always(x), always(x))
Эта реализация tapF
надеется f
вернуть будущее, и оно заставит его разрешить с исходным входным значением.
Затем его можно использовать для регистрации, например:
request.get("http://mywebsite.com")
.chain( tapF(res => myLogF(`res is: ${res}`)) )
.chain( saveDB )
.chain( tapF(() => myLogF("Saved!")) )
Теперь результат этого выражения полностью независим от того, что происходит внутри tapF
функции.
Я считаю, что должен ответить на ваши первые два вопроса. Последний; "Каков наиболее часто используемый шаблон для журналов?", Я не уверен в этом. Есть несколько моделей, и две, которые я могу придумать:
- Вход в качестве побочного эффекта внутри монады, которая представляет побочный эффект. Это то, что мы сделали с
tapF
, - Сбор журналов в памяти с помощью Writer Monad и запись их на краю программы. У меня нет опыта с этим подходом.