Last active
March 16, 2017 10:23
-
-
Save char0n/1c5f85e5f66ec1d5a19e907b468b01ef to your computer and use it in GitHub Desktop.
Calls effect until predicate is satisfied or max interactions are hit.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const { curry, ifElse, when, toString } = require('ramda'); | |
const { range, isError, negate, flow } = require('lodash/fp'); | |
const delay = require('delay'); | |
const rejectP = Promise.reject.bind(Promise); | |
const resolveP = Promise.resolve.bind(Promise); | |
const toError = when(negate(isError), flow(toString, Error)); | |
// Calls effect until predicate is satisfied or max interactions are hit. | |
// callUntil :: Promise b => (a -> Boolean) -> Options -> (... -> a) -> b | |
// Options = { interval: Number, max: Number } | |
const callUntil = curry((predicate, { interval = 1000, max = 5 } = {}, effect) => (...args) => { | |
const thunk = () => effect(...args); | |
const effectDissatisfied = () => rejectP(new Error('Effect did not satisfied the predicate')); | |
const plan = queue => queue.then(thunk).then(when(predicate, rejectP)).then(delay(interval)); | |
return new Promise((resolve, reject) => { | |
// Generate plan for effect execution in advance. | |
range(0, max).reduce(plan, resolveP()).then(effectDissatisfied).catch(ifElse(isError, reject, resolve)); | |
}); | |
}); |
ubnt-michals
commented
Mar 15, 2017
Čistě přes mergeMap
'use strict';
const { Observable } = require('rxjs/Rx');
const { curry, ifElse } = require('ramda');
// Calls effect until predicate is satisfied or max interactions are hit.
// callUntil :: Promise b => (a -> Boolean) -> { interval, max } -> (... -> b)
const callUntil = curry((predicate, { interval = 2000, max = 5 } = {}, effect) => (...args) =>
Observable.defer(() => effect(...args))
.mergeMap(ifElse(predicate, Observable.of, Observable.throw))
.retryWhen(errors => errors
.scan((errorCount, err) => { if (errorCount >= max) { throw err } return errorCount + 1 }, 1)
.delay(interval)
)
.toPromise()
);
const iAmHappy = isHappy => isHappy;
let happinessCheckTimes = 0;
const checkHappiness = times => new Promise((resolve) => {
console.log(new Date(), 'checking happiness');
happinessCheckTimes += 1;
setTimeout(() => { resolve(happinessCheckTimes > times) }, 200);
});
const happyCall = callUntil(iAmHappy, { interval: 2000, max: 5 }, checkHappiness);
happyCall(7).then(() => console.log('Happy'), () => console.log('Still Sad'));
Njn rxjs prostre skrati tu implementaci na polovinu ;]
Ale je pekne videt jak rxjs ma build-in operatory presne na problemy ktere jsem resil ciste funkcionalnim kodem.
Updatlnul jsem a vyrazne skratil a zdjednosil funkcionalni verzi. Jediny scenar ktery zustava divny je ze pokud effect returnuje error a predicate checkuje na error tak outer premisa bude rejecnuta, misto toho aby byla resolvnuta errorem. Kazdopadne, aktualni chovani mi prijde logicke.
Prochazel jsem jeste ty rxjs varianty:
- prvni varianta spoleha nato ze effect ma thenable interface, coz ale neni pravda
- jak druha varianta resi to ze effect muze sam hodit v svym tele exception nebo vratit v thenable interfacu promise reject ?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment