Skip to content

Instantly share code, notes, and snippets.

@char0n
Last active March 16, 2017 10:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save char0n/1c5f85e5f66ec1d5a19e907b468b01ef to your computer and use it in GitHub Desktop.
Save char0n/1c5f85e5f66ec1d5a19e907b468b01ef to your computer and use it in GitHub Desktop.
Calls effect until predicate is satisfied or max interactions are hit.
'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
Copy link

'use strict';

const { Observable } = require('rxjs/Rx');
const { curry, unless } = require('ramda');

const rejectP = Promise.reject.bind(Promise);

// 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).then(unless(predicate, rejectP)))
    .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'));

@ubnt-michals
Copy link

Č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'));

@char0n
Copy link
Author

char0n commented Mar 15, 2017

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.

@char0n
Copy link
Author

char0n commented Mar 15, 2017

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