import { noop } from 'lodash'; type CancelOperation = () => void; type Operation = (fulfill: (t: T) => void, reject: (e: E) => void) => CancelOperation | void; /** * A Task is a container for an operation. * * The result of the operation can be accessed using operators such as map. The * Task can be executed using the run method. */ export class Task { _operation: Operation; static of(t: T) { return new Task(fulfill => void fulfill(t)); } static reject(e: E) { return new Task((fulfill, reject) => void reject(e)); } static fromPromise(callback: () => Promise) { return new Task(function(fulfill, reject) { callback().then(fulfill, reject); }); } constructor(operation: Operation) { this._operation = operation; } run(onFulfilled: (t: T) => void = noop, onRejected: (e: E) => void = noop) { let isCancelled = false; function onFulfilledProxy(t: T) { if (!isCancelled) onFulfilled(t); } function onRejectedProxy(e: E) { if (!isCancelled) onRejected(e); } let _cancel = this._operation(onFulfilledProxy, onRejectedProxy); return function cance() { isCancelled = true; if (_cancel) (_cancel)(); } } map(func: (t: T) => U) { return new Task((fulfill, reject) => { return this.run((value) => void fulfill(func(value)), reject); }); } mapRejected(func: (e: E) => F) { return new Task((fulfill, reject) => { return this.run(fulfill, (e) => void reject(func(e))); }) } chain(func: (t: T) => Task) { return new Task((fulfill, reject) => { let innerCancel: CancelOperation | void; let outerCancel = this.run( (value) => { innerCancel = func(value).run( fulfill, reject); }, reject ); return () => { if (outerCancel) (outerCancel as CancelOperation)(); if (innerCancel) (innerCancel as CancelOperation)(); }; }); } chainRejected(func: (e: E) => Task) { return new Task((fulfill, reject) => { let innerCancel: CancelOperation | void; let outerCancel = this.run( fulfill, (error) => { innerCancel = func(error).run(fulfill, reject) } ); return () => { if (outerCancel) (outerCancel as CancelOperation)(); if (innerCancel) (innerCancel as CancelOperation)(); }; }) } }