Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import ExecutionContext from "../async/ExecutionContext";
- import {None, Option, Some} from "../monads/Option";
- import Try from "../monads/Try";
- /**
- * This class is a super simplified implementation of Scala futures. This difference lies in the
- * fact that since implicit context is not used here, the chain of futures must be run explicitly
- * through the run method. For the same reason, all void combinators, like onSuccess, return the
- * type Future <T>. In practice, it looks like this:
- *
- * {{{
- * new Future<number>(() => {
- * console.log("calc");
- * return 99;
- * }, executor)
- * .map((v) => {
- * return v + 100;
- * })
- * .onFailed((e) => {
- * console.log(`Error = ${e.message}`)
- * })
- * .onSuccess((v) => {
- * console.log(`Result = ${v}`)
- * })
- * .run();
- * }}}
- *
- * Also this class implements the functionality of promise. In order to create a promise, you need to make
- * a call to Future.Promise (). This call will return an unfired future that behaves just like a normal one,
- * but must run by the method success or failed. The principle of operation of these methods is identical for
- * all types of promise. Example:
- *
- * {{{
- * let p = Future.Promise<number>(executor)
- * .map((v) => {
- * return v + 100;
- * })
- * .onSuccess((v: number) => {
- * console.log(`Result = ${v}`)
- * })
- * .onFailed((e) => {
- * console.log(`Error = ${e.message}`)
- * });
- *
- * console.log("Some before execution code...");
- * p.success(99);
- * }}}
- *
- */
- export default class Future<T> {
- /** Next operation after complete the computation. This value contain function, witch call running the
- * next chained future */
- next: () => void = null;
- /** Parent of the future. Root future has no parent */
- parent: Future<any> = null;
- /** Chain executor */
- executor: ExecutionContext;
- /** Function with computation. For root future this is an app code function presented in constructor.
- * Fot any other chained future this value contains a wrapper derived be the combinator functions witch
- * apply to the result of the previous future computations */
- f: () => T = null;
- /** Computation result */
- value: Option<Try<T>> = new None();
- /** Create new future
- *
- * @param {() => T} f function for asynchronous execution
- * @param {ExecutionContext} executor execution context
- */
- constructor (f: () => T, executor: ExecutionContext) {
- this.executor = executor;
- this.f = f;
- }
- /** Create already completed future
- *
- * @param {T} value value for completion
- * @param {ExecutionContext} executor execution context
- * @return {Future<T>} completed future
- */
- static Successful<T>(value: T, executor: ExecutionContext): Future<T> {
- let fut = new Future<T>(null, executor);
- fut.value = new Some(Try.doWork(() => value));
- return fut;
- }
- /** Create new future as promise. This type of future does not perform calculations on its own.
- * She expects that they will be presented to her from the outside. The run method is not used
- * to start the futures. Instead, there are special methods, success and failed. Through them
- * and provides the result of filling the promise.
- *
- * @param {ExecutionContext} executor
- * @return {Future<T>}
- * @constructor
- */
- static Promise<T>(executor: ExecutionContext): Future<T> {
- let fut = new Future<T>(null, executor);
- fut.f = () => {
- if (fut.value.get().isSuccess()) {
- return fut.value.get().get();
- } else {
- throw fut.value.get().failed().get();
- }
- };
- return fut;
- }
- /** Run futures chain for execution
- *
- * @return {Future<T>} final future in the chain
- */
- run(): Future<T> {
- this.execRoot();
- return this;
- }
- /** Present result to the promise
- *
- * @param value result
- */
- success(value: any) {
- this.execRootPromise(Try.doWork(() => value))
- }
- /** Fail promise with error
- *
- * @param {Error} error exception for failing
- */
- failed(error: Error) {
- this.execRootPromise(Try.doWork(() => { throw error }))
- }
- map<S>(f: (T) => S): Future<S> {
- let from = this;
- let fut = new Future<S>(null, this.executor);
- fut.parent = this;
- this.next = () => {
- if (from.value.get().isSuccess()) {
- fut.value = new Some(Try.doWork(() => f(from.value.get().get())));
- } else {
- fut.value = new Some(Try.doWork(() => {
- throw from.value.get().failed().get();
- }));
- }
- fut.flatRun();
- };
- return fut;
- }
- onSuccess<S>(f: (T) => S): Future<S> {
- let from = this;
- let fut = new Future<S>(() => {
- return f(from.value.get());
- }, this.executor);
- fut.parent = this;
- this.next = () => {
- if (from.value.get().isSuccess()) {
- fut.value = new Some(Try.doWork(() => f(from.value.get().get())));
- } else {
- fut.value = new Some(Try.doWork(() => {
- throw from.value.get().failed().get();
- }));
- }
- fut.flatRun();
- };
- return fut;
- }
- onFailed<S>(f: (T) => S): Future<S> {
- let from = this;
- let fut = new Future<S>(() => {
- return f(from.value.get());
- }, this.executor);
- fut.parent = this;
- this.next = () => {
- if (from.value.get().isFailure()) {
- fut.value = new Some(Try.doWork(() => {
- f(from.value.get().failed().get());
- throw from.value.get().failed().get();
- }));
- } else {
- fut.value = from.value as Option<Try<any>> as Option<Try<S>>;
- /*fut.value = new Some(Try.doWork(() => {
- //fut.value = new Some(from.value.get().get());
- //throw from.value.get().get();
- }))*/
- }
- fut.flatRun();
- };
- return fut;
- }
- //------------------------------- INTERNAL API ------------------------------------
- /** Execute future on the execution context */
- private flatRun() {
- if (this.value.isEmpty()) {
- this.executor.registerForExecution(() => {
- if (this.f != null) {
- this.value = new Some(Try.doWork(() => this.f()));
- }
- if (this.next != null) {
- this.next();
- }
- });
- } else {
- if (this.next != null) {
- this.next();
- }
- }
- }
- /** Go through the futures chain to the root future and start it execution */
- private execRoot() {
- if (this.parent == null) {
- this.flatRun();
- } else {
- this.parent.execRoot();
- }
- }
- /** Used if future is promise Go through the futures chain to the root future and start it execution
- * with presented value */
- private execRootPromise(value: Try<T>) {
- if (this.parent == null) {
- this.value = new Some(value);
- this.flatRun();
- } else {
- this.parent.execRootPromise(value);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement