view Implab/PromiseAll`1.cs @ 255:b00441e04738 v3

Adde workaround to the behaviour of the logical operations stack in conjuction with async/await methods
author cin
date Wed, 04 Apr 2018 15:38:48 +0300
parents 5cb4826c2c2a
children
line wrap: on
line source

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Implab {
    class PromiseAll<T> : IResolvable {

        int m_count;

        readonly List<IPromise<T>> m_promises = new List<IPromise<T>>();

        readonly Deferred<T[]> m_deferred;

        IPromise<T[]> m_result;

        readonly Func<T, IPromise> m_cleanup;

        readonly Action m_cancel;

        public bool Done {
            get { return m_deferred.Promise.IsResolved && m_cleanup == null; }
        }

        public IPromise<T[]> ResultPromise {
            get { return m_result; }
        }

        public void AddPromise(IPromise<T> promise) {
            Interlocked.Increment(ref m_count);
            promise.Then(this);
        }

        public PromiseAll(Deferred<T[]> deferred, Func<T, IPromise> cleanup, Action cancel) {
            m_deferred = deferred;
            m_cancel = cancel;
            m_cleanup = cleanup;
        }

        public void Resolve() {
            if (Interlocked.Decrement(ref m_count) == 0)
                m_deferred.Resolve(GetResults());
        }

        public void Reject(Exception error) {
            m_deferred.Reject(error);
        }

        public void Complete() {
            if (m_cancel != null || m_cleanup != null)
                m_result = m_deferred.Promise.Catch(CleanupResults);
            else
                m_result = m_deferred.Promise;
        }

        IPromise<T[]> CleanupResults(Exception reason) {
            var errors = new List<Exception>();
            errors.Add(reason);

            if (m_cancel != null)
                try {
                    m_cancel();
                } catch (Exception e) {
                    errors.Add(e);
                }

            if (m_cleanup != null) {
                return Promise.All(
                    m_promises.Select(p => p
                        .Then(m_cleanup, e => { })
                        .Catch(e => {
                            errors.Add(e);
                        })
                    )
                ).Then<T[]>(new Func<T[]>(() => {
                    throw new AggregateException(errors);
                }), (Func<Exception, T[]>)null);
            } else {
                return Promise.Reject<T[]>(errors.Count > 1 ? new AggregateException(errors) : reason);
            }
        }

        T[] GetResults() {
            var results = new T[m_promises.Count];
            for (var i = 0; i < results.Length; i++)
                results[i] = m_promises[i].Join();
            return results;
        }
    }
}