gnar-edge
Version:
A sharp set of utilities: base64, drain, handleChange, jwt, notifications
55 lines (45 loc) • 2.33 kB
JavaScript
const drain = (gf, ...args) => {
// Note: These two tests are valid in both ES6 and ES6 transpiled to ES5 via Babel.
const GENERATOR_FUNCTION = 'GeneratorFunction';
const generatorFunctionTest = o => o.constructor.name === GENERATOR_FUNCTION;
const generatorTest = o => o.constructor.constructor.name === GENERATOR_FUNCTION;
if (!generatorFunctionTest(gf)) {
throw new TypeError(`Drain expects a generator function - received "${gf}".`);
}
const iterate = g => new Promise((resolve, reject) => {
const toPromise = obj => {
const fnToPromise = fn => new Promise((res, rej) => {
try { res(fn()); } catch (e) { rej(e); }
});
const arrayToPromise = arr => Promise.all(arr.map(toPromise));
const objectToPromise = o => Promise.all(Object.values(o).map(toPromise)).then(
values => Object.keys(o).reduce((memo, key, idx) => {
memo[key] = values[idx];
return memo;
}, {})
);
return [
{ /* falsy */ test: o => !o, result: o => Promise.resolve(o) },
{ /* promise */ test: o => o instanceof Promise, result: o => o },
{ /* generator function */ test: generatorFunctionTest, result: o => iterate(o()) },
{ /* generator */ test: generatorTest, result: o => iterate(o) },
{ /* function */ test: o => typeof o === 'function', result: o => fnToPromise(o) },
{ /* array */ test: o => Array.isArray(o), result: o => arrayToPromise(o) },
{ /* literal object */ test: o => o.toString() === '[object Object]', result: o => objectToPromise(o) },
{ /* default */ test: o => o, result: o => Promise.resolve(o) }
]
.find(({ test }) => test(obj)).result(obj);
};
const next = ({ done, value }) => (done ? resolve(value) : toPromise(value).then(
resolved => { try { next(g.next(resolved)); } catch (e) { reject(e); } },
exception => { try { next(g.throw(exception)); } catch (e) { reject(e); } }
));
return next(g.next(...args));
});
const thenable = () => iterate(gf(...args));
thenable.then = (onFulfilled, onRejected) => thenable().then(onFulfilled, onRejected);
thenable.catch = onRejected => thenable().catch(onRejected);
thenable.finally = onFinally => thenable().finally(onFinally);
return thenable;
};
export default drain;