Permalink
Browse files

(new task) Adds .orElse and .mapRejected to Task

  • Loading branch information...
robotlolita committed Apr 7, 2017
1 parent 4b05ec2 commit 35fbd0d220454578eec365ad8309ba4c6f3d0b48
@@ -244,15 +244,17 @@ As a convenience for combining a large or unknown amount of tasks, the `waitAll(
const { waitAll } = require('folktale/data/task');
const result3 = await delay(10).and(delay(20).and(delay(30))).run().promise();
$ASSERT(result == [10, [20, 30]]);
$ASSERT(result3 == [10, [20, 30]]);
const result4 = await waitAll([
delay(10),
delay(20),
delay(30)
]).run().promise();
$ASSERT(result == [10, 20, 30]);
$ASSERT(result4 == [10, 20, 30]);
## Error handling
Sometimes processes will fail. You can recover from such failures using the `.orElse()` method. The method takes a function, passes to it the error value, if one happened, and expects it to return a new Task, whose state will be assimilated. In order to recover from the error you'd return a successful task, so computations that depend on it may proceed.
@@ -0,0 +1,5 @@
@annotate: folktale.data.task._Task
category: Constructing
---
A Task.
@@ -0,0 +1,5 @@
@annotate: folktale.data.task._Task.prototype
category: Special values
---
The container for instance methods for the Task structure.
View
@@ -82,6 +82,27 @@ class Task {
);
}
/*~
* stability: experimental
* type: |
* forall e1, e2, v, r:
* (Task e1 v r).((e1) => e2) => Task e2 v r
*/
mapRejected(transformation) {
return new Task(
resolver => {
const execution = this.run();
execution.listen({
onCancelled: resolver.cancel,
onRejected: reason => resolver.reject(transformation(reason)),
onResolved: resolver.resolve
});
return execution;
},
execution => execution.cancel()
);
}
/*~
* stability: experimental
* type: |
@@ -165,6 +186,34 @@ class Task {
);
}
/*~
* stability: experimental
* type: |
* forall e, e2, v, r1, r2:
* (Task e v r1).((e) => Task e2 v r2) => Task e2 v r2
*/
orElse(handler) {
return new Task(
resolver => {
const execution = this.run();
execution.listen({
onCancelled: resolver.cancel,
onResolved: resolver.resolve,
onRejected: reason => {
handler(reason).run().listen({
onCancelled: resolver.cancel,
onRejected: resolver.reject,
onResolved: resolver.resolve
});
}
});
return execution;
},
execution => execution.cancel()
);
}
/*~
* stability: experimental
* type: |
View
@@ -14,7 +14,8 @@ const Task = require('./_task');
* name: module folktale/data/task
*/
module.exports = {
...Task,
of: Task.of,
rejected: Task.rejected,
task: require('./task'),
waitAny: require('./wait-any'),
waitAll: require('./wait-all'),
@@ -9,6 +9,11 @@
const { of } = require('./_task');
/*~
* stability: experimental
* type: |
* forall v, e: ([Task e v Any]) => Task e [v] Any
*/
const waitAll = (tasks) => {
if (tasks.length === 0) {
throw new Error('Task.waitAll() requires a non-empty array of tasks.');
@@ -8,6 +8,11 @@
//----------------------------------------------------------------------
/*~
* stability: experimental
* type: |
* forall v, e: ([Task e v Any]) => Task e v Any
*/
const waitAny = (tasks) => {
if (tasks.length === 0) {
throw new Error(`Task.waitAny() requires a non-empty array of tasks.`);
@@ -37,7 +37,20 @@ describe('Data.Task', () => {
});
property('#chain(f) ignores cancellations', 'nat', 'nat -> task nat', env, (a, f) => {
const execution = Task.task(r => r.cancel()).run();
const execution = Task.task(r => r.cancel()).chain(f).run();
return execution.future() ::eq(cancelled());
});
property('#orElse(f) transforms failed tasks', 'nat', 'nat -> task nat', env, (a, f) => {
return Task.rejected(a).orElse(f).run().future() ::eq(f(a).run().future());
});
property('#orElse(f) ignores successful tasks', 'nat', 'nat -> task nat', env, (a, f) => {
return Task.of(a).orElse(f).run().future() ::eq(Future.of(a));
});
property('#orElse(f) ignores cancellations', 'nat', 'nat -> task nat', env, (a, f) => {
const execution = Task.task(r => r.cancel()).orElse(f).run();
return execution.future() ::eq(cancelled());
});
@@ -50,7 +63,19 @@ describe('Data.Task', () => {
});
property('#map(f) ignores cancellations', 'nat', 'nat -> nat', (a, f) => {
return Task.task(r => r.cancel()).run().future() ::eq(cancelled());
return Task.task(r => r.cancel()).map(f).run().future() ::eq(cancelled());
});
property('#mapRejected(f) transforms rejected tasks', 'nat', 'nat -> nat', (a, f) => {
return Task.rejected(a).mapRejected(f).run().future() ::eq(Future.rejected(f(a)));
});
property('#mapRejected(f) ignores successes', 'nat', 'nat -> nat', (a, f) => {
return Task.of(a).mapRejected(f).run().future() ::eq(Future.of(a));
});
property('#mapRejected(f) ignores cancellations', 'nat', 'nat -> nat', (a, f) => {
return Task.task(r => r.cancel()).mapRejected(f).run().future() ::eq(cancelled());
});
property('#apply(a) applies successes', 'nat', 'nat -> nat', (a, f) => {

0 comments on commit 35fbd0d

Please sign in to comment.