src/promise.ts (91 lines of code) (raw):
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import * as List from './list';
interface SharedState<T> {
value?: T;
thenHandlers: {(val: T): void}[];
}
export function pending<T>(): Promise<T> {
return new Promise<T>({value: undefined, thenHandlers: []});
}
export function resolved<T>(val: T): Promise<T> {
return new Promise<T>({value: val, thenHandlers: []});
}
export class Promise<T> {
private sharedState: SharedState<T>;
constructor(sharedState: SharedState<T>) {
this.sharedState = sharedState;
}
getFuture(): Future<T> {
return new Future<T>(this.sharedState);
}
setValue(val: T) {
this.sharedState.value = val;
this.sharedState.thenHandlers.forEach(function (f) {
f(val);
});
}
}
export class Future<T> {
/**
The state that is shared between the future and the promise. You would
really like this ot be "friend" for the exported functions but there is
no way to do this inside of typescript. Nobody should touch it, hence the
`_` indicating that this should be treated as an implementation detail.
*/
public _sharedState: SharedState<T>;
/**
A Future<T> should never be allocated apart from reading one from a promise
using getFuture or a combinator from another future. Ideally this
constructor could be marked private for the module but typescript does not
allow for that.
*/
constructor(sharedState: SharedState<T>) {
this._sharedState = sharedState;
}
map<U>(f: (v: T) => U): Future<U> {
return map(f, this);
}
}
export function then<T>(f: (v: T) => void, future: Future<T>): void {
if (future._sharedState.value === undefined) {
future._sharedState.thenHandlers.push(f);
} else {
f(future._sharedState.value);
}
}
export function map<T, U>(f: (v: T) => U, future: Future<T>): Future<U> {
const pendingPromise = pending<U>();
then(function (val: T) {
pendingPromise.setValue(f(val));
}, future);
return pendingPromise.getFuture();
}
export function mbind<T, U>(
f: (v: T) => Future<U>,
future: Future<T>,
): Future<U> {
const pendingPromise: Promise<U> = pending<U>();
then(function (val: T) {
then(function (val2: U) {
pendingPromise.setValue(val2);
}, f(val));
}, future);
return pendingPromise.getFuture();
}
export function munit<T>(t: T): Future<T> {
return resolved(t).getFuture();
}
export function aapply<T, U>(
futureFunc: Future<(a: T) => U>,
futureVal: Future<T>,
): Future<U> {
return mbind(function (t: T) {
return map(function (f: (a: T) => U) {
return f(t);
}, futureFunc);
}, futureVal);
}
export function all<T>(futures: List.List<Future<T>>): Future<List.List<T>> {
const partialFutures = List.map(function (future: Future<T>) {
return map(function (val: T) {
return (result: List.List<T>) => List.cons(val, result);
}, future);
}, futures);
return List.foldr(
function (
soFarFuture: Future<List.List<T>>,
thisFuture: Future<(list: List.List<T>) => List.List<T>>,
) {
return aapply(thisFuture, soFarFuture);
},
resolved<List.List<T>>(List.of<T>()).getFuture(),
partialFutures,
);
}