UNPKG

gnar-edge

Version:

A sharp set of utilities: base64, drain, handleChange, jwt, notifications

55 lines (45 loc) 2.33 kB
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;