-
-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #830
- Loading branch information
Showing
4 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { curryN, bind } from 'ramda'; | ||
import curry1 from 'ramda/src/internal/_curry1'; | ||
|
||
import resolveP from './resolveP'; | ||
import rejectP from './rejectP'; | ||
|
||
/** | ||
* Takes a generator function and returns an async function. | ||
* The async function returned is a curried function whose arity matches that of the generator function. | ||
* | ||
* Note: This function is handy for environments that does support generators but doesn't support async/await. | ||
* | ||
* @func async | ||
* @memberOf RA | ||
* @since {@link https://char0n.github.io/ramda-adjunct/2.16.0|v2.16.0} | ||
* @category Function | ||
* @sig Promise c => (a, b, ...) -> a -> b -> ... -> c | ||
* @param {Function} generatorFn The generator function | ||
* @return {Function} Curried async function | ||
* @see {@link https://www.promisejs.org/generators/} | ||
* @example | ||
* | ||
* const asyncFn = RA.async(function* generator(val1, val2) { | ||
* const a = yield Promise.resolve(val1); | ||
* const b = yield Promise.resolve(val2); | ||
* | ||
* return a + b; | ||
* }); | ||
* | ||
* asyncFn(1, 2); //=> Promise(3) | ||
* | ||
*/ | ||
const async = curry1(generatorFn => { | ||
function asyncWrapper(...args) { | ||
const iterator = bind(generatorFn, this)(...args); | ||
|
||
const handle = result => { | ||
const resolved = resolveP(result.value); | ||
|
||
return result.done | ||
? resolved | ||
: resolved.then( | ||
value => handle(iterator.next(value)), | ||
error => handle(iterator.throw(error)) | ||
); | ||
}; | ||
|
||
try { | ||
return handle(iterator.next()); | ||
} catch (error) { | ||
return rejectP(error); | ||
} | ||
} | ||
|
||
if (generatorFn.length > 0) { | ||
return curryN(generatorFn.length, asyncWrapper); | ||
} | ||
return asyncWrapper; | ||
}); | ||
|
||
export default async; |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import * as R from 'ramda'; | ||
import { assert } from 'chai'; | ||
|
||
import eq from './shared/eq'; | ||
import * as RA from '../src'; | ||
|
||
describe('async', function() { | ||
context('given wrapping of generator', function() { | ||
specify('should mimic async/await behavior', async function() { | ||
const asyncFn = RA.async(function* generator(val1, val2) { | ||
const a = yield RA.resolveP(val1); | ||
const b = yield RA.resolveP(val2); | ||
|
||
return a + b; | ||
}); | ||
const expected = await asyncFn(1, 2); | ||
|
||
eq(expected, 3); | ||
}); | ||
|
||
context('and the generator throw Error', function() { | ||
specify('should resolve with rejection', async function() { | ||
const asyncFn = RA.async(function* generator(val1, val2) { | ||
yield RA.resolveP(val1); | ||
yield RA.resolveP(val2); | ||
|
||
throw new Error('generator error'); | ||
}); | ||
|
||
try { | ||
await asyncFn(1, 2); | ||
throw new Error('fulfilling should fail'); | ||
} catch (error) { | ||
assert.instanceOf(error, Error); | ||
eq(error.message, 'generator error'); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
it('should support yield delegation', async function() { | ||
const foo = function* generator(val1, val2) { | ||
const a = yield RA.resolveP(val1); | ||
const b = yield RA.resolveP(val2); | ||
|
||
return a + b; | ||
}; | ||
const bar = RA.async(function* generator(val1, val2) { | ||
const a = yield RA.resolveP(val1); | ||
const b = yield RA.resolveP(val2); | ||
const c = yield* foo(a, b); | ||
|
||
return c + 3; | ||
}); | ||
|
||
eq(await bar(1, 2), 6); | ||
}); | ||
|
||
it('should support async delegation', async function() { | ||
const foo = RA.async(function* generator(val1, val2) { | ||
const a = yield RA.resolveP(val1); | ||
const b = yield RA.resolveP(val2); | ||
|
||
return a + b; | ||
}); | ||
const bar = RA.async(function* generator(val1, val2) { | ||
const a = yield RA.resolveP(val1); | ||
const b = yield RA.resolveP(val2); | ||
const c = yield foo(a, b); | ||
|
||
return c + 3; | ||
}); | ||
|
||
eq(await bar(1, 2), 6); | ||
}); | ||
|
||
it('should support recursion delegation', async function() { | ||
const async = RA.async(function* generator(val) { | ||
let newVal = val; | ||
|
||
if (val > 1) { | ||
newVal = yield async(val - 1); | ||
} | ||
|
||
return yield newVal; | ||
}); | ||
|
||
eq(await async(10), 1); | ||
}); | ||
|
||
it('should curry', async function() { | ||
const async = RA.async(R.__); | ||
const asyncFn = async(function* generator() { | ||
yield RA.resolveP(1); | ||
return 2; | ||
}); | ||
const expected = await asyncFn(); | ||
|
||
eq(expected, 2); | ||
}); | ||
|
||
context('given wrapping of generator with arity of 2', function() { | ||
let asyncFn; | ||
|
||
beforeEach(function() { | ||
// eslint-disable-next-line require-yield | ||
asyncFn = RA.async(function* generator(a, b) { | ||
return a + b; | ||
}); | ||
}); | ||
|
||
specify('should translate generator arity to wrapper', function() { | ||
eq(asyncFn.length, 2); | ||
}); | ||
|
||
specify('should curry wrapper to appropriate arity', async function() { | ||
eq(await asyncFn(1, 2), 3); | ||
eq(await asyncFn(1)(2), 3); | ||
}); | ||
}); | ||
|
||
context('given wrapping of generator with arity of 0', function() { | ||
context('then the resulting wrapper', function() { | ||
let asyncFn; | ||
|
||
beforeEach(function() { | ||
// eslint-disable-next-line require-yield | ||
asyncFn = RA.async(function* generator() { | ||
return 1; | ||
}); | ||
}); | ||
|
||
specify('should not support placeholder', async function() { | ||
const expected = await asyncFn(R.__); | ||
|
||
eq(expected, 1); | ||
}); | ||
|
||
specify('should support call without arguments', async function() { | ||
const expected = await asyncFn(); | ||
|
||
eq(expected, 1); | ||
}); | ||
}); | ||
}); | ||
}); |