Last active
December 12, 2015 04:08
-
-
Save hisui/4712504 to your computer and use it in GitHub Desktop.
Promise+Future for JavaScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare module sf { | |
export interface Future<T> extends AsyncEither<Error,T> { | |
lhs():FutureProjection_L<T>; | |
rhs():FutureProjection_R<T>; | |
flatMap<B>(f: (e:Either<Error,T>) => AsyncEither<Error,B>):Future<B>; | |
map<B>(f: (e:Either<Error,T>) => Either<Error,B>):Future<B>; | |
each(f: (e:Either<Error,T>) => void):Future<T>; | |
} | |
export interface Subject<T> extends Future<T> { | |
new ():Subject<T>; | |
notifyFail(e:Error):void; | |
notifyDone(o:T) :void; | |
} | |
export interface FutureProjection_L<A> { | |
flatMap<B>(f: (e:Error) => Future<B>):Future<B>; | |
map(f: (e:Error) => Error):Future<A>; | |
each(f: (e:Error) => void):Future<A>; | |
} | |
export interface FutureProjection_R<A> { | |
flatMap<B>(f: (e:A) => Future<B>):Future<B>; | |
map<B>(f: (e:A) => B):Future<B>; | |
each(f: (o:A) => void):Future<A>; | |
} | |
export interface EitherProjection_L<L,R> { | |
flatMap<B>(f: (e:L) => Either<B,R>):Either<B,R>; | |
map<B>(f: (e:L) => B):Either<B,R>; | |
each(f: (e:L) => void):Either<L,R>; | |
} | |
export interface EitherProjection_R<L,R> { | |
flatMap<B>(f: (e:R) => Either<L,B>):Either<L,B>; | |
map<B>(f: (e:R) => B):Either<L,B>; | |
each(f: (o:R) => void):Either<L,R>; | |
} | |
export interface AsyncEither<L,R> { | |
isCompleted():boolean; | |
cancel():void; | |
} | |
export interface Either<L,R> extends AsyncEither<L,R> { | |
lhs():EitherProjection_L<L,R>; | |
rhs():EitherProjection_R<L,R>; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is conceptual implementation of monadic promise/future system for JavaScript | |
// https://gist.github.com/hisui/4712504 | |
(function () | |
{ | |
"use strict"; | |
/** | |
* mix-in: モナドの基本的なオペレーションを提供します。 | |
* @param M<A> | |
* @param A | |
*/ | |
this["MonadOps"] || ( | |
this["MonadOps"] = (function () { | |
// console.log("add monad ops => "+ this); | |
/** | |
* @param X | |
* @param e: X | |
* @return M<X> | |
*/ | |
this.pure = function (e) { throw new Error("Unimplemented!") }; | |
/** | |
* @param B | |
* @param f: A => M<B> | |
* @return M<B> | |
*/ | |
this.flatMap = function (f) { throw new Error("Unimplemented!") }; | |
/** | |
* @param B | |
* @param f: A => B | |
* @return M<B> | |
*/ | |
this.map = function (f) | |
{ | |
return this.flatMap( | |
(function (e) { return this.pure(f(e)) }).bind(this)); | |
}; | |
/** | |
* @param f: A => any | |
* @return M<A> | |
*/ | |
this.each = function (f) | |
{ | |
return this.map(function (e) { return (f(e), e) }); | |
}; | |
/** | |
* @param B | |
* @param f: M<A> => M<B> | |
* @return M<B> | |
*/ | |
this.$ || (this.$ = function (f) { return f(this) }); | |
/** | |
* @param (T..) = A | |
* @param f: (T..) => M<B> | |
* @return M<B> | |
*/ | |
this.flatMapN = function (f) | |
{ | |
return this.flatMap(function (args) { return f.apply(null, args) }); | |
}; | |
/** | |
* @param (T..) = A | |
* @param f: (T..) => B | |
* @return M<B> | |
*/ | |
this.mapN = function (f) | |
{ | |
return this.map(function (args) { return f.apply(null, args) }); | |
}; | |
/** | |
* @param (T..) = A | |
* @param f: (T..) => any | |
*/ | |
this.eachN = function (f) | |
{ | |
return this.each(function (args) { f.apply(null, args) }); | |
}; | |
/** | |
* @param B | |
* @param M<B> = A | |
* @return M<B> | |
*/ | |
this.flatten = function () { return this.flatMap(function (e) { return e }) }; | |
/** | |
* @return M<(A)> | |
*/ | |
this.pack = function () { return this.map(function (e) { return [e] }) }; | |
/** | |
* @param (T..) = A | |
* @param e: M<B> | |
* @return M<(T..,B)> | |
*/ | |
this.push = function (that) | |
{ | |
this.flatMap(function (a) { return that.map(function (b) { return [a, b] }) }); | |
}; | |
/** | |
* @param name: String | |
* @param args: Array<any> | |
* @return M<any> | |
*/ | |
this.send = function (name, args) | |
{ | |
return this.map(function (e) { return e[name].apply(e, args) }); | |
}; | |
})); | |
/** | |
* monkey-patching: MonadOps to Function | |
*/ | |
(function (proto) { | |
MonadOps.call(proto); | |
/** | |
* @override MonadOps | |
*/ | |
proto.flatMap = function (g) | |
{ | |
var f = this; | |
return function () | |
{ | |
return g(f.apply(null, Array.prototype.slice.apply(arguments)))() | |
}; | |
}; | |
/** | |
* @override MonadOps | |
*/ | |
proto.pure = function (e) { return function () { return e } }; | |
/** | |
* 定義されてなかったらそれっぽいのを突っ込みます。 | |
*/ | |
proto.bind || (proto.bind = function (context) | |
{ | |
var func = this; | |
var args = Array.prototype.slice.call(arguments, 1); | |
return (function () | |
{ | |
return func.apply(context, args.concat | |
(Array.prototype.slice.call(arguments))) | |
}); | |
}); | |
/** | |
* | |
*/ | |
proto.extends = function (base, traits) | |
{ | |
traits || (traits = {}); | |
var ctor = this; | |
function stub() { | |
this.constructor = ctor; | |
} | |
for (var key in ctor.prototype) { | |
traits[key] || (traits[key] = ctor.prototype[key]); | |
} | |
stub.prototype = base.prototype; | |
ctor.prototype = new stub(); | |
for (var key in traits) { | |
ctor.prototype[key] = traits[key]; | |
} | |
return ctor; | |
}; | |
/** | |
* @param T | |
* @return T => T | |
*/ | |
Function.identity || ( | |
Function.identity = function (e) { return e } ); | |
})(Function.prototype); | |
/** | |
* 非同期の処理を表現します。 | |
* @param V | |
*/ | |
this["FutureLike"] || ( | |
this["FutureLike"] = (function (base) { | |
function FutureLike() | |
{ | |
base.call(this); | |
} | |
return FutureLike.extends(base, | |
{ | |
/** | |
* この処理が終了しているかどうかです。 | |
* @return Boolean | |
*/ | |
isCompleted: function () { throw new Error("Not implemented!") }, | |
/** | |
* この処理が終了した時に呼び出されるコールバックを設定します。 | |
* @param f: V => any | |
*/ | |
wait: function (f) { throw new Error("Not implemented!") }, | |
/** | |
* 結果値を返します。この処理が終了していない場合、例外をスローします。 | |
* @return V | |
*/ | |
sync: function () { throw new Error("Not implemented!") }, | |
/** | |
* この処理をキャンセルします。 | |
*/ | |
cancel: function () {} | |
}); | |
})(Object)); | |
/** | |
* mix-in: {@link FutureLike#sync()}が自分自身を返すような{@link FutureLike}です。 | |
* @super FutureLike<T> | |
* @param T | |
*/ | |
this["FutureThis"] || ( | |
this["FutureThis"] = (function () { | |
/** | |
* override FutureLike<T> | |
*/ | |
this.sync = function () | |
{ | |
if (!this.isCompleted()) { | |
throw new Error("This task is running!"); | |
} | |
return this; | |
}; | |
})); | |
/** | |
* mix-in: 常に終了した状態(isCompleted())の{@link FutureThis}です。 | |
* @super FutureThis<T> | |
* @param T | |
*/ | |
this["FutureEnds"] || ( | |
this["FutureEnds"] = (function () { | |
/** | |
* @override FutureLike<T> | |
*/ | |
this.isCompleted = function () { return true }; | |
/** | |
* @override FutureLike<T> | |
*/ | |
this.wait = function (f) { f(this) }; | |
})); | |
/** | |
* 非同期の{@link Either}です。 | |
* @super FutureLike<Either<L,R>> | |
* @param L | |
* @param R | |
*/ | |
this["AsyncEither"] || ( | |
this["AsyncEither"] = (function (base) { | |
function AsyncEither() | |
{ | |
base.call(this); | |
} | |
return AsyncEither.extends(base, | |
{ | |
/** | |
* @return AsyncEitherProjectionL<L,R> | |
*/ | |
lhs: function () { throw new Error("Not implemented!") }, | |
/** | |
* @return AsyncEitherProjectionR<L,R> | |
*/ | |
rhs: function () { throw new Error("Not implemented!") }, | |
/** | |
* "lhs" or "rhs" | |
* @return String | |
*/ | |
dir: function () { throw new Error("Not implemented!") } | |
}); | |
})(FutureLike)); | |
/** | |
* 非同期の{@link Maybe}です。 | |
* @super MonadOps<AsyncMaybe,T>, FutureLike<Maybe<T>> | |
* @param T | |
*/ | |
this["AsyncMaybe"] || ( | |
this["AsyncMaybe"] = (function (base) { | |
MonadOps.call(AsyncMaybe.prototype); | |
function AsyncMaybe() | |
{ | |
base.call(this); | |
} | |
return AsyncMaybe.extends(base, | |
{ | |
/** | |
* @return Boolean | |
*/ | |
isDefined: function () { throw new Error("Not implemented!") }, | |
/** | |
* @param B <: T | |
* @param f: => AsyncMaybe<B> | |
* @return AsyncMaybe<T> | |
*/ | |
orElse: function (f) | |
{ | |
return this.isDefined() ? this: f(); | |
}, | |
/** | |
* このオブジェクトが保持する値を返します。 | |
* @return T | |
*/ | |
get: function () { throw new Error("Not implemented!") }, | |
/** | |
* @param e: T | |
* @return T | |
*/ | |
getOr: function (e) { return this.isDefined() ? this.get(): e } | |
}); | |
})(FutureLike)); | |
/** | |
* オプショナルな値を格納します。 | |
* @super AsyncMaybe<T>, FutureEnds<Maybe<T>> | |
* @param T | |
*/ | |
this["Maybe"] || ( | |
this["Maybe"] = (function (base) { | |
FutureThis.call(Maybe.prototype); | |
FutureEnds.call(Maybe.prototype); | |
function Some(e) | |
{ | |
Maybe.call(this), this._raw = e; | |
} | |
Some.extends(Maybe, | |
{ | |
/** | |
* @override MonadOps<AsyncMaybe,T> | |
*/ | |
flatMap: function (f) { return f(this._raw) }, | |
/** | |
* @override AsyncMaybe<T> | |
*/ | |
get: function () { return this._raw }, | |
/** | |
* @return String | |
*/ | |
toString: function () { return "Some("+ this._raw +")" } | |
}); | |
function None() { Maybe.call(this) } | |
None.extends(Maybe, | |
{ | |
/** | |
* @override MonadOps<AsyncMaybe,T> | |
*/ | |
flatMap: function (f) { return this }, | |
/** | |
* @override Maybe<T> | |
*/ | |
get: function () { throw new Error("No such element!") }, | |
/** | |
* @return String | |
*/ | |
toString: function () { return "None" } | |
}); | |
FutureEnds.call(Maybe.prototype); | |
function Maybe(e) | |
{ | |
if (!(this instanceof arguments.callee)) { | |
return arguments.length > 0 ? new Some(e): new None(); | |
} | |
base.call(this); | |
} | |
return Maybe.extends(base, | |
{ | |
/** | |
* @override MonadOps<AsyncMaybe,T> | |
*/ | |
pure: function (e) { return new Some(e) }, | |
/** | |
* @override AsyncMaybe<T> | |
*/ | |
isDefined: function () { return this instanceof Some } | |
}); | |
})(AsyncMaybe)); | |
/** | |
* @super MonadOps<AsyncEither<L|R>,L|R> | |
* @param L | |
* @param R | |
*/ | |
this["AsyncEitherProjection"] || ( | |
this["AsyncEitherProjection"] = (function (base) { | |
function AsyncEitherProjection(src, dir) | |
{ | |
if (!(dir === "lhs" || dir === "rhs")) { | |
throw new Error("bug? (*_*)"); | |
} | |
base.call(this); | |
this._src = src; | |
this._dir = dir; | |
} | |
return AsyncEitherProjection.extends(base, | |
{ | |
/** | |
* @override AsyncMaybe | |
*/ | |
isDefined: function () { return this._src.dir() === this._dir }, | |
/** | |
* @override AsyncMaybe | |
*/ | |
get: function () { return this.sync().get() }, | |
/** | |
* @override FutureLike<EitherProjection<L,R>> | |
*/ | |
isCompleted: function () { return this._src.isCompleted() }, | |
/** | |
* @override FutureLike<EitherProjection<L,R>> | |
*/ | |
wait: function (f) | |
{ | |
this._src.wait((function (e) { f(e[this._dir]) }).bind(this)); | |
}, | |
/** | |
* @override FutureLike<EitherProjection<L,R>> | |
*/ | |
sync: function () { return this._src.sync()[this._dir]() }, | |
/** | |
* @override FutureLike<EitherProjection<L,R>> | |
*/ | |
cancel: function () { this._src.cancel() }, | |
/** | |
* @return AsyncEither<L,R> | |
*/ | |
e: function () { return this._src }, | |
/** | |
* @override MonadOps<AsyncEither<L,R>,AsyncEither<L,R>> | |
*/ | |
pure: function (e) | |
{ | |
return Future[{rhs: "done", lhs: "fail"}[this._dir]](e) | |
}, | |
/** | |
* @override MonadOps<AsyncEither<L,R>,AsyncEither<L,R>> | |
*/ | |
flatMap: function (f) | |
{ | |
var subj = new Subject(); | |
this._src.wait((function (src) | |
{ | |
src[this._dir]().flatMap(f).wait(subj.emit.bind(subj)) | |
}).bind(this)); | |
return subj; | |
}, | |
/** | |
* @override MonadOps<AsyncEither<L,R>,AsyncEither<L,R>> | |
*/ | |
push: function (that) | |
{ | |
return Future.tuple(this.e(), that.e()) | |
[this._dir]().mapN(function (a, b) { return a.concat(b) }); | |
} | |
}); | |
})(AsyncMaybe)); | |
/** | |
* @super AsyncEitherProjection<L|R>, FutureEnds<EitherProjection> | |
* @param L | |
* @param R | |
*/ | |
this["EitherProjection"] || ( | |
this["EitherProjection"] = (function (base) { | |
FutureEnds.call(EitherProjection.prototype); | |
function EitherProjection(src, dir) | |
{ | |
base.call(this, src, dir); | |
} | |
return EitherProjection.extends(base, | |
{ | |
/** | |
* @override AsyncMaybe | |
*/ | |
get: function () { return this.isDefined() ? this._src.raw(): Maybe().get() }, | |
/** | |
* @override MonadOps<AsyncEither<L,R>,Either<L,R>> | |
*/ | |
pure: function (e) { return Either[this._dir](e) }, | |
/** | |
* @override MonadOps<AsyncEither<L,R>,Either<L,R>> | |
*/ | |
flatMap: function (f) { return this.isDefined() ? f(this._src.raw()): this._src } | |
}); | |
})(AsyncEitherProjection)); | |
/** | |
* 左か右のどちらかの値を保持します。 | |
* @super AsyncEither<L,R>, FutureEnds<Either> | |
* @param L | |
* @param R | |
*/ | |
this["Either"] || ( | |
this["Either"] = (function (base) { | |
FutureThis.call(Either.prototype); | |
FutureEnds.call(Either.prototype); | |
function Either(raw, dir) | |
{ | |
if (!(dir === "lhs" || dir === "rhs")) { | |
throw new Error("bug? (*_*)"); | |
} | |
base.call(this); | |
this._raw = raw; | |
this._dir = dir; | |
} | |
Either.lhs = function (e) { return new Either(e, "lhs") }; | |
Either.rhs = function (e) { return new Either(e, "rhs") }; | |
return Either.extends(base, | |
{ | |
/** | |
* このオブジェクトが保持する値を返します。 | |
* @internal | |
*/ | |
raw: function () { return this._raw }, | |
/** | |
* @override AsyncEither<L,R> | |
*/ | |
lhs: function () { return new EitherProjection(this, "lhs") }, | |
/** | |
* @override AsyncEither<L,R> | |
*/ | |
rhs: function () { return new EitherProjection(this, "rhs") }, | |
/** | |
* @override AsyncEither<L,R> | |
*/ | |
dir: function () { return this._dir }, | |
/** | |
* @return String | |
*/ | |
toString: function () { return "Either("+ this._dir +":"+ this._raw +")" } | |
}); | |
})(AsyncEither)); | |
/** | |
* 完了後にエラーまたは値を生成する非同期処理を表現します。 | |
* @super MonadOps<AsyncEither<Error,?>,Either<Error,T>>, AsyncEither<Error,T> | |
* @param T | |
*/ | |
this["Future"] || ( | |
this["Future"] = (function (base) { | |
function Future(e) { | |
if (e && !(e instanceof Either)) { | |
throw new Error("bug? (*_*) :: "+ e); | |
} | |
base.call(this); | |
this.done = e || null; | |
this.cons = function (e) {}; | |
this.disp = 0; | |
} | |
Future.done = function (e) { return new Future(Either.rhs(e)) }; | |
Future.fail = function (e) { return new Future(Either.lhs(e)) }; | |
function cancelAll(l) { | |
for (var j = 0; j < l.length; ++j) { | |
if (l[j] !== null) l[j].cancel(); | |
} | |
} | |
/** | |
* {@link Future#cancel()}した場合、エラーとして通知される値です。 | |
*/ | |
Future.canceled = new Error("cancel"); | |
/** | |
* 終了しない{@link Future}を作成します。 | |
* @return Future<Null> | |
*/ | |
Future.never = function () { return new Subject() }; | |
/** | |
* @param T.. | |
* @param a..: Future<T>.. | |
* @return Future<(T..)> | |
*/ | |
Future.tuple = function () { | |
var subj = new Subject(); | |
var done = 0; | |
var args = Array.prototype.slice.call(arguments); | |
var dest = []; | |
for (var i = 0; i < args.length; ++i) { | |
(function (i) { | |
args[i].get(function (e) | |
{ | |
args[i] = null; | |
if (subj !== null) { | |
if (e.dir() === "lhs") { | |
subj.emit(e); | |
subj = null; | |
cancelAll(args); | |
return; | |
} | |
dest[i] = e.sync().raw(); | |
if (++done == args.length) { | |
subj.notifyDone(dest); | |
subj = null; | |
} | |
} | |
}) | |
})(i); | |
} | |
return subj; | |
}; | |
/** | |
* @param T | |
* @param a..: Future<T>.. | |
* @return Future<T> | |
*/ | |
Future.first = function () { | |
var subj = new Subject(); | |
var args = Array.prototype.slice.call(arguments); | |
for (var i = 0; i < args.length; ++i) { | |
(function (i) { | |
args[i].get(function (e) | |
{ | |
args[i] = null; | |
if (subj !== null) { | |
subj.emit(e); | |
subj = null; | |
cancelAll(args); | |
} | |
}); | |
})(i); | |
} | |
return subj; | |
}; | |
return Future.extends(base, | |
{ | |
/** | |
* 非同期値をコールバック関数を通して取得します。 | |
* @param f: Either<Error,T> => any | |
*/ | |
get: function (head) | |
{ | |
if(this.isCompleted()) { | |
setTimeout(head, 0, this.done); | |
return; | |
} | |
var tail = this.cons; | |
this.cons = function (e) { head(e), tail(e) }; | |
}, | |
/** | |
* @param U | |
* @param f: Either<Error,T> => AsyncEither<Error,U> | |
* @return Future<U> | |
*/ | |
map: function (f) | |
{ | |
var that = this; | |
var subj = new Subject(function () { | |
that.cancel(); | |
}); | |
this.wait(function (e) { (that = f(e)); | |
that.wait(function (e) { | |
subj._dispatch(e, Future.canceled === e.lhs().getOr(null)); | |
}); | |
}); | |
return subj; | |
}, | |
/** | |
* @param f: Either<Error,T> => void | |
*/ | |
each: function (f) | |
{ | |
return this.map(function (e) { return (f(e), e) }); | |
}, | |
/** | |
* @override AsyncEither<Error,T> | |
*/ | |
lhs: function () { return new AsyncEitherProjection(this, "lhs") }, | |
/** | |
* @override AsyncEither<Error,T> | |
*/ | |
rhs: function () { return new AsyncEitherProjection(this, "rhs") }, | |
/* | |
* @override AsyncEither<Error,T> | |
*/ | |
dir: function () { return this.sync().dir() }, | |
/** | |
* @override FutureLike<Either<Error,T>> | |
*/ | |
sync: function () | |
{ | |
if (!this.isCompleted()) { | |
throw new Error("This task is running!"); | |
} | |
return this.done; | |
}, | |
/** | |
* @override FutureLike<Either<Error,T>> | |
*/ | |
isCompleted: function () { return this.done !== null }, | |
/** | |
* @override FutureLike<Either<Error,T>> | |
*/ | |
wait: function (f) { this.get((function (_) { f(this.done) }).bind(this)) }, | |
/** | |
* @override FutureLike<Either<Error,T>> | |
*/ | |
cancel: function () | |
{ | |
if (this.disp != -1 && this._queryCancel()) { | |
this._dispatch(Either.lhs(Future.canceled), true); | |
} | |
}, | |
/** | |
* @protected | |
*/ | |
_queryCancel: function () { return true }, | |
/** | |
* @protected | |
*/ | |
_dispatch: function (e, interrupt) | |
{ | |
if(!((e = e.sync()) instanceof Either)) { | |
throw new Error("bug? (*_*)"); | |
} | |
if (this.disp != -1 && (!this.disp || interrupt)) { | |
this.disp++; | |
setTimeout((function (disp) { | |
if (this.disp == disp) { | |
this.disp = -1; | |
this.cons(e); | |
this.cons = null; | |
} | |
}).bind(this), 0, this.disp); | |
} | |
this.done = e; | |
}, | |
/** | |
* @return String | |
*/ | |
toString: function () { return "Future("+ (this.done || "?") +")" } | |
}); | |
})(AsyncEither)); | |
/** | |
* ユーザー定義の{@link Future}を作成するためのインターフェイスです。 | |
* @super Future<T> | |
* @param T | |
*/ | |
this["Subject"] || ( | |
this["Subject"] = (function (base) { | |
function Subject(canceler) | |
{ | |
base.call(this); | |
if (canceler) { | |
this._queryCancel = canceler; | |
} | |
} | |
return Subject.extends(base, | |
{ | |
/** | |
* @param e: T | |
*/ | |
notifyFail: function (e) { this.emit(Either.lhs(e)) }, | |
/** | |
* @param e: Error | |
*/ | |
notifyDone: function (e) { this.emit(Either.rhs(e)) }, | |
/** | |
* @param e: Either<Error,T> | |
*/ | |
emit: function (e) { this._dispatch(e) } | |
}); | |
})(Future)); | |
/** | |
* @param T | |
* @param f: => T | |
* @return AsyncEither<Error,T> | |
*/ | |
this["Try"] = function (f) | |
{ | |
try { | |
return Either.rhs(f()); | |
} catch(e) { | |
return Either.lhs((e)); | |
} | |
}; | |
/** | |
* @param T | |
* @param X.. | |
* @param f: ((X..) => any) => any | |
* @return ((X..) => T) => Future<T> | |
*/ | |
this["Cps"] = function (f) | |
{ | |
return (function (g) | |
{ | |
var subj = new Subject(); | |
f(function () { | |
subj.emit(Try(g.bind.apply(g, [null].concat | |
(Array.prototype.slice.call(arguments))))) | |
}); | |
return subj; | |
}) | |
}; | |
}).call(this); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>promise.js</title> | |
<script src="promise.js"></script> | |
<script> | |
chain1(); | |
chain2(); | |
chain3(); | |
tuple(); | |
tupleAndCancel(); | |
function chain1() | |
{ | |
var a = new Subject(); | |
var b = new Subject(); | |
var c = new Subject(); | |
a | |
.lhs().each(function (e) { console.log("OMG:"+ e) }) | |
.rhs().flatMap(function (e) | |
{ | |
console.log("flatMap-1:"+ e); | |
return b; | |
}) | |
.lhs().each(function (e) { console.log("omg:"+ e) }) | |
.rhs().flatMap(function (e) | |
{ | |
console.log("flatMap-2:"+ e); | |
return c; | |
}) | |
.lhs().each(function (e) { console.log("err:"+ e) }) | |
.rhs().each(function (e) { console.log("res:"+ e) }); | |
a.notifyDone("AAA"); | |
b.notifyDone("BBB"); | |
c.notifyDone("CCC"); | |
} | |
function chain2() | |
{ | |
var a = new Subject(); | |
var b = new Subject(); | |
var c = new Subject(); | |
a | |
.lhs().each(function (e) { console.log("OMG:"+ e) }) | |
.rhs().flatMap(function (e) | |
{ | |
console.log("flatMap-1:"+ e); | |
return b; | |
}) | |
.lhs().each(function (e) { console.log("omg:"+ e) }) | |
.rhs().flatMap(function (e) | |
{ | |
console.log("flatMap-2:"+ e); | |
return c; | |
}) | |
.lhs().each(function (e) { console.log("err:"+ e) }) | |
.rhs().each(function (e) { console.log("res:"+ e) }); | |
a.notifyDone("AAA"); | |
b.notifyFail(new Error("BBB")); | |
} | |
function chain3() | |
{ | |
var a = new Subject(); | |
var b = new Subject(); | |
// recover | |
a | |
.lhs().flatMap(function (e) | |
{ | |
console.log("error!? let's recover..:"+ e.message); | |
setTimeout(function () | |
{ | |
b.notifyDone("Yeah"); | |
}, 100); | |
return b; | |
}) | |
.rhs().each(function (e) | |
{ | |
console.log("finished! "+ e); | |
}); | |
a.notifyFail(new Error("OMG")); | |
} | |
function tuple() | |
{ | |
var x = new Subject(); | |
var y = new Subject(); | |
var z = new Subject(); | |
setTimeout(function () { x.notifyDone("XXX") }, 300); | |
setTimeout(function () { y.notifyDone("YYY") }, 999); | |
setTimeout(function () { z.notifyDone("ZZZ") }, 200); | |
Future.tuple(x, y, z) | |
.rhs().each(function (e) | |
{ | |
console.log("tuple -> "+ e); | |
}); | |
} | |
function tupleAndCancel() | |
{ | |
var x = new Subject(); | |
var y = new Subject(); | |
var z = new Subject(); | |
setTimeout(function () { x.notifyFail(new Error("xxx")) }, 100); | |
setTimeout(function () { y.notifyDone("YYY") }, 999); | |
setTimeout(function () { z.notifyDone("ZZZ") }, 200); | |
Future.tuple(x, y, z) | |
.rhs().each(function (e) { console.log("tuple -> "+ e) }) | |
.lhs().each(function (e) { console.log("error -> "+ e) }); | |
x.lhs().each(function (e) { console.log("x error: "+ e) }); | |
y.lhs().each(function (e) { console.log("y error: "+ e) }); | |
z.lhs().each(function (e) { console.log("z error: "+ e) }); | |
} | |
</script> | |
</head> | |
<body> | |
<h1>promise.js</h1> | |
<div> | |
<a href="https://gist.github.com/hisui/4712504">gist</a> | |
</div> | |
</body> | |
</html> |
AsyncEither#lhs, rhsはAsyncOptionを継承するAsyncProjectionL, AsyncProjectionRを返すようにした方が良いかも。。
AsyncProjection#bindに渡す関数はAsyncEither返すようにしたほうがいい
fusion: (Future<A>, Future<B>) => Future<(A,B)>
こういうの作る予定
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Eitherかますのは冗長やろか・・・?