diff --git a/src/Task.ts b/src/Task.ts index 4f4ad604e..57256baac 100644 --- a/src/Task.ts +++ b/src/Task.ts @@ -53,7 +53,7 @@ export interface Task { * @category natural transformations * @since 2.0.0 */ -export const fromIO: FromIO1['fromIO'] = (ma) => () => Promise.resolve(ma()) +export const fromIO: FromIO1['fromIO'] = (ma) => () => Promise.resolve().then(ma) // ------------------------------------------------------------------------------------- // combinators @@ -90,7 +90,7 @@ export function delay(millis: number): (ma: Task) => Task { new Promise((resolve) => { setTimeout(() => { // tslint:disable-next-line: no-floating-promises - ma().then(resolve) + Promise.resolve().then(ma).then(resolve) }, millis) }) } @@ -119,7 +119,8 @@ const _chain: Chain1['chain'] = (ma, f) => pipe(ma, chain(f)) * @category Functor * @since 2.0.0 */ -export const map: (f: (a: A) => B) => (fa: Task) => Task = (f) => (fa) => () => fa().then(f) +export const map: (f: (a: A) => B) => (fa: Task) => Task = (f) => (fa) => () => + Promise.resolve().then(fa).then(f) /** * Apply a function to an argument under a type constructor. @@ -128,7 +129,7 @@ export const map: (f: (a: A) => B) => (fa: Task) => Task = (f) => (f * @since 2.0.0 */ export const ap: (fa: Task) => (fab: Task<(a: A) => B>) => Task = (fa) => (fab) => () => - Promise.all([fab(), fa()]).then(([f, a]) => f(a)) + Promise.all([Promise.resolve().then(fab), Promise.resolve().then(fa)]).then(([f, a]) => f(a)) /** * @category Pointed @@ -143,7 +144,9 @@ export const of: Pointed1['of'] = (a) => () => Promise.resolve(a) * @since 2.0.0 */ export const chain: (f: (a: A) => Task) => (ma: Task) => Task = (f) => (ma) => () => - ma().then((a) => f(a)()) + Promise.resolve() + .then(ma) + .then((a) => f(a)()) /** * Derivable from `Chain`. @@ -199,7 +202,7 @@ declare module './HKT' { */ export function getRaceMonoid(): Monoid> { return { - concat: (x, y) => () => Promise.race([x(), y()]), + concat: (x, y) => () => Promise.race([Promise.resolve().then(x), Promise.resolve().then(y)]), empty: never } } @@ -480,7 +483,7 @@ export const ApT: Task = */ export const traverseReadonlyNonEmptyArrayWithIndex = (f: (index: number, a: A) => Task) => ( as: ReadonlyNonEmptyArray -): Task> => () => Promise.all(as.map((a, i) => f(i, a)())) as any +): Task> => () => Promise.all(as.map((a, i) => Promise.resolve().then(() => f(i, a)()))) as any /** * Equivalent to `ReadonlyArray#traverseWithIndex(ApplicativePar)`. @@ -505,12 +508,16 @@ export const traverseReadonlyNonEmptyArrayWithIndexSeq = (f: (index: numbe _.tail(as).reduce>>( (acc, a, i) => acc.then((bs) => - f(i + 1, a)().then((b) => { - bs.push(b) - return bs - }) + Promise.resolve() + .then(f(i + 1, a)) + .then((b) => { + bs.push(b) + return bs + }) ), - f(0, _.head(as))().then(_.singleton) + Promise.resolve() + .then(f(0, _.head(as))) + .then(_.singleton) ) /** diff --git a/test/Task.ts b/test/Task.ts index 174b7b649..76a64083f 100644 --- a/test/Task.ts +++ b/test/Task.ts @@ -5,7 +5,7 @@ import * as RA from '../src/ReadonlyArray' import * as _ from '../src/Task' import * as assert from 'assert' import * as S from '../src/string' -import { ReadonlyNonEmptyArray } from '../src/ReadonlyNonEmptyArray' +import * as RNEA from '../src/ReadonlyNonEmptyArray' const delayReject = (n: number, a: A): _.Task => () => new Promise((_, reject) => { @@ -33,6 +33,18 @@ const assertOp = (f: (a: _.Task, b: _.Task) => _.Task) => asyn } describe('Task', () => { + // ------------------------------------------------------------------------------------- + // safety + // ------------------------------------------------------------------------------------- + it('stack-safe', async () => { + const doProcessing = (number: number) => _.of(number * 2) + const pipeline = pipe(_.of(RNEA.range(1, 55000)), _.chain(RNEA.traverse(_.ApplicativeSeq)(doProcessing))) + + const res = await pipeline() + + expect(res.length).toBe(55000) + }) + // ------------------------------------------------------------------------------------- // pipeables // ------------------------------------------------------------------------------------- @@ -165,7 +177,7 @@ describe('Task', () => { }) describe('array utils', () => { - const input: ReadonlyNonEmptyArray = ['a', 'b'] + const input: RNEA.ReadonlyNonEmptyArray = ['a', 'b'] it('traverseReadonlyArrayWithIndex', async () => { const f = _.traverseReadonlyArrayWithIndex((i, a: string) => _.of(a + i))