using System;
using System.Diagnostics;
namespace Implab {
///
/// Класс для асинхронного получения результатов. Так называемое "обещание".
///
/// Тип получаемого результата
///
/// Сервис при обращении к его методу дает обещаиние о выполнении операции,
/// клиент получив такое обещание может установить ряд обратных вызово для получения
/// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.
///
/// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
/// данные события клиент должен использовать методы Then.
///
///
/// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
/// использует методы Resolve либо Reject для оповещения клиетна о
/// выполнении обещания.
///
///
/// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
/// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
/// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
/// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
/// обещания.
///
///
/// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
/// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
/// использовать соответствующую форму методе Then.
///
///
/// Также хорошим правилом является то, что Resolve и Reject должен вызывать
/// только инициатор обещания иначе могут возникнуть противоречия.
///
///
public class Promise : AbstractPromise>, IPromise, IDeferred {
class StubDeferred : IDeferred {
public static readonly StubDeferred instance = new StubDeferred();
StubDeferred() {
}
#region IDeferred implementation
public void Resolve(T value) {
}
public void Reject(Exception error) {
}
#endregion
#region ICancellable implementation
public void Cancel() {
}
#endregion
}
class RemapDescriptor : IDeferred {
readonly Func m_remap;
readonly Func m_failed;
readonly Func m_cancel;
readonly IDeferred m_deferred;
public RemapDescriptor(Func remap, Func failed, Func cancel, IDeferred deferred ) {
Debug.Assert(deferred != null);
m_remap = remap;
m_failed = failed;
m_cancel = cancel;
m_deferred = deferred;
}
#region IDeferred implementation
public void Resolve(T value) {
if (m_remap != null) {
try {
m_deferred.Resolve(m_remap(value));
} catch (Exception ex) {
Reject(ex);
}
}
}
public void Reject(Exception error) {
if (m_failed != null) {
try {
m_deferred.Resolve(m_failed(error));
} catch (Exception ex) {
m_deferred.Reject(ex);
}
} else {
m_deferred.Reject(error);
}
}
#endregion
#region ICancellable implementation
public void Cancel() {
if (m_cancel != null) {
try {
m_deferred.Resolve(m_cancel());
} catch (Exception ex) {
Reject(ex);
}
} else {
m_deferred.Cancel();
}
}
#endregion
}
class ListenerDescriptor : IDeferred {
readonly Action m_handler;
readonly PromiseEventType m_events;
public ListenerDescriptor(Action handler, PromiseEventType events) {
Debug.Assert(handler != null);
m_handler = handler;
m_events = events;
}
#region IDeferred implementation
public void Resolve(T value) {
if (m_events.HasFlag(PromiseEventType.Success)) {
try {
m_handler();
// Analysis disable once EmptyGeneralCatchClause
} catch {
}
}
}
public void Reject(Exception error) {
if (m_events.HasFlag(PromiseEventType.Error)){
try {
m_handler();
// Analysis disable once EmptyGeneralCatchClause
} catch {
}
}
}
#endregion
#region ICancellable implementation
public void Cancel() {
if (m_events.HasFlag(PromiseEventType.Cancelled)){
try {
m_handler();
// Analysis disable once EmptyGeneralCatchClause
} catch {
}
}
}
#endregion
}
class ValueEventDescriptor : IDeferred {
readonly Action m_success;
readonly Action m_failed;
readonly Action m_cancelled;
readonly IDeferred m_deferred;
public ValueEventDescriptor(Action success, Action failed, Action cancelled, IDeferred deferred) {
Debug.Assert(deferred != null);
m_success = success;
m_failed = failed;
m_cancelled = cancelled;
m_deferred = deferred;
}
#region IDeferred implementation
public void Resolve(T value) {
if (m_success != null) {
try {
m_success(value);
m_deferred.Resolve(value);
} catch (Exception ex) {
Reject(ex);
}
}
}
public void Reject(Exception error) {
if (m_failed != null) {
try {
m_failed(error);
m_deferred.Resolve(default(T));
} catch(Exception ex) {
m_deferred.Reject(ex);
}
} else {
m_deferred.Reject(error);
}
}
#endregion
#region ICancellable implementation
public void Cancel() {
if (m_cancelled != null) {
try {
m_cancelled();
m_deferred.Resolve(default(T));
} catch(Exception ex) {
Reject(ex);
}
} else {
m_deferred.Cancel();
}
}
#endregion
}
public class EventDescriptor : IDeferred {
readonly Action m_success;
readonly Action m_failed;
readonly Action m_cancelled;
readonly IDeferred m_deferred;
public EventDescriptor(Action success, Action failed, Action cancelled, IDeferred deferred) {
Debug.Assert(deferred != null);
m_success = success;
m_failed = failed;
m_cancelled = cancelled;
m_deferred = deferred;
}
#region IDeferred implementation
public void Resolve(T value) {
if (m_success != null) {
try {
m_success();
m_deferred.Resolve(value);
} catch (Exception ex) {
Reject(ex);
}
}
}
public void Reject(Exception error) {
if (m_failed != null) {
try {
m_failed(error);
m_deferred.Resolve(default(T));
}catch (Exception ex)
{
m_deferred.Reject(ex);
}
} else {
m_deferred.Reject(error);
}
}
#endregion
#region ICancellable implementation
public void Cancel() {
if (m_cancelled != null) {
try {
m_cancelled();
m_deferred.Resolve(default(T));
} catch (Exception ex) {
Reject(ex);
}
} else {
m_deferred.Cancel();
}
}
#endregion
}
T m_result;
public virtual void Resolve(T value) {
if (BeginSetResult()) {
m_result = value;
EndSetResult();
}
}
public void Reject(Exception error) {
SetError(error);
}
public Type PromiseType {
get {
return typeof(T);
}
}
public new T Join() {
WaitResult(-1);
return m_result;
}
public new T Join(int timeout) {
WaitResult(timeout);
return m_result;
}
public IPromise On(Action success, Action error, Action cancel) {
AddHandler(new ValueEventDescriptor(success, error, cancel, StubDeferred.instance));
return this;
}
public IPromise On(Action success, Action error) {
AddHandler(new ValueEventDescriptor(success, error, null, StubDeferred.instance));
return this;
}
public IPromise On(Action success) {
AddHandler(new ValueEventDescriptor(success, null, null, StubDeferred.instance));
return this;
}
public IPromise On(Action handler, PromiseEventType events) {
Listen(events, handler);
return this;
}
public IPromise Then(Func mapper, Func error, Func cancel) {
var promise = new Promise();
if (mapper != null)
promise.On(Cancel, PromiseEventType.Cancelled);
AddHandler(new RemapDescriptor(mapper, error, cancel, promise));
return promise;
}
public IPromise Then(Func mapper, Func error) {
var promise = new Promise();
if (mapper != null)
promise.On(Cancel, PromiseEventType.Cancelled);
AddHandler(new RemapDescriptor(mapper, error, null, promise));
return promise;
}
public IPromise Then(Func mapper) {
var promise = new Promise();
if (mapper != null)
promise.On(Cancel, PromiseEventType.Cancelled);
AddHandler(new RemapDescriptor(mapper, null, null, promise));
return promise;
}
public IPromise Chain(Func> chained, Func> error, Func> cancel) {
// this promise will be resolved when an asyc operation is started
var promise = new Promise>();
AddHandler(new RemapDescriptor>(
chained,
error,
cancel,
promise
));
var medium = new Promise();
if (chained != null)
medium.On(Cancel, PromiseEventType.Cancelled);
// we need to connect started async operation with the medium
// if the async operation hasn't been started by the some reason
// report is to the medium
promise.On(
result => ConnectPromise(result, medium),
medium.Reject,
medium.Cancel
);
return medium;
}
static void ConnectPromise(IPromise result, Promise medium) {
if (result != null) {
result.On(
medium.Resolve,
medium.Reject,
() => medium.Reject(new OperationCanceledException())
);
medium.On(result.Cancel, PromiseEventType.Cancelled);
} else {
medium.Reject(
new NullReferenceException(
"The chained asynchronous operation returned" +
" 'null' where the promise instance is expected"
)
);
}
}
public IPromise Chain(Func> chained, Func> error) {
return Chain(chained, error, null);
}
public IPromise Chain(Func> chained) {
return Chain(chained, null, null);
}
public IPromise Error(Func error) {
var promise = new Promise();
if (error != null)
On(
(Action)null,
ex => {
try {
promise.Resolve(error(ex));
} catch (Exception ex2) {
promise.Reject(ex2);
}
}
);
else
Listen(PromiseEventType.Error, () => promise.Resolve(default(T2)));
return promise;
}
public IPromise Cancelled(Func handler) {
var promise = new Promise();
if (handler != null)
On(
(Action)null,
null,
() => {
try {
promise.Resolve(handler());
} catch (Exception ex) {
promise.Reject(ex);
}
});
else
Listen(PromiseEventType.Cancelled, () => promise.Resolve(default(T2)));
return promise;
}
public IPromise Then(Action success, Action error, Action cancel) {
var promise = new Promise();
if (success != null)
promise.On(Cancel, PromiseEventType.Cancelled);
AddHandler(new EventDescriptor(success, error, cancel, promise));
return promise;
}
public IPromise Then(Action success, Action error) {
return Then(success, error, null);
}
public IPromise Then(Action success) {
return Then(success, null, null);
}
public IPromise Chain(Func chained, Func error, Func cancel) {
var promise = new Promise();
AddHandler(
new RemapDescriptor(
x => chained(),
error,
cancel,
promise
)
);
var medium = new Promise();
if (chained != null)
medium.On(Cancel, PromiseEventType.Cancelled);
promise.On(
result => ConnectPromise(result, medium),
medium.Reject,
medium.Cancel
);
return medium;
}
static void ConnectPromise(IPromise result, Promise medium) {
if (result != null) {
result.On(
medium.Resolve,
medium.Reject,
() => medium.Reject(new OperationCanceledException())
);
medium.On(result.Cancel, PromiseEventType.Cancelled);
} else {
medium.Reject(
new NullReferenceException(
"The chained asynchronous operation returned" +
" 'null' where the promise instance is expected"
)
);
}
}
public IPromise Chain(Func chained, Func error) {
return Chain(chained, error, null);
}
public IPromise Chain(Func chained) {
return Chain(chained, null, null);
}
public IPromise On(Action success, Action error, Action cancel) {
AddHandler(new EventDescriptor(success,error,cancel, StubDeferred.instance));
return this;
}
public IPromise On(Action success, Action error) {
AddHandler(new EventDescriptor(success, error, null, StubDeferred.instance));
return this;
}
public IPromise On(Action success) {
Listen(PromiseEventType.Success, success);
return this;
}
IPromise IPromise.On(Action handler, PromiseEventType events) {
Listen(events,handler);
return this;
}
public IPromise Error(Action error) {
var promise = new Promise();
if (error != null)
On(
(Action)null,
ex => {
try {
error(ex);
promise.Resolve();
} catch (Exception ex2) {
promise.Reject(ex2);
}
});
else
Listen(PromiseEventType.Error, promise.Resolve);
return promise;
}
public IPromise Cancelled(Action handler) {
var promise = new Promise();
if (handler != null)
On(
(Action)null,
null,
() => {
try {
handler();
promise.Resolve();
} catch (Exception ex) {
promise.Reject(ex);
}
});
else
Listen(PromiseEventType.Cancelled, promise.Resolve);
return promise;
}
public IPromise Cast() {
return (IPromise)this;
}
#region implemented abstract members of AbstractPromise
protected override void SignalSuccess(IDeferred handler) {
handler.Resolve(m_result);
}
protected override void SignalError(IDeferred handler, Exception error) {
handler.Reject(error);
}
protected override void SignalCancelled(IDeferred handler) {
handler.Cancel();
}
protected override void Listen(PromiseEventType events, Action handler) {
if (handler != null)
AddHandler(new ListenerDescriptor(handler, events));
}
#endregion
public static IPromise ResultToPromise(T value) {
var p = new Promise();
p.Resolve(value);
return p;
}
public static IPromise ExceptionToPromise(Exception error) {
var p = new Promise();
p.Reject(error);
return p;
}
}
}