"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Mutex = void 0;
const Array_1 = require("./Array");
const Deferred_1 = require("./Deferred");
/**
 * Aggregate promises efficiently
 */
class Mutex {
    constructor() {
        this._pushCount = 0;
        this._arr = [];
    }
    get arr() {
        (0, Array_1.filterInPlace)(this._arr, (ea) => ea.pending);
        return this._arr;
    }
    get pushCount() {
        return this._pushCount;
    }
    push(f) {
        this._pushCount++;
        const p = f();
        // Don't cause awaitAll to die if a task rejects:
        this.arr.push(new Deferred_1.Deferred().observeQuietly(p));
        return p;
    }
    /**
     * Run f() after all prior-enqueued promises have resolved.
     */
    serial(f) {
        return this.push(() => this.awaitAll().then(() => f()));
    }
    /**
     * Only run f() if all prior have finished, otherwise, no-op and wait until
     * all pending have resolved.
     */
    runIfIdle(f) {
        return this.pending ? undefined : this.serial(f);
    }
    get pendingCount() {
        // Don't need vacuuming, so we can use this._arr:
        return this._arr.reduce((sum, ea) => sum + (ea.pending ? 1 : 0), 0);
    }
    get pending() {
        return this.pendingCount > 0;
    }
    get settled() {
        // this.arr is a getter that does vacuuming
        return this.arr.length === 0;
    }
    /**
     * @return a promise that will be resolved when all previously-pushed Promises
     * are resolved. Any promise rejection will throw the whole chain.
     */
    awaitAll() {
        return this.arr.length === 0
            ? Promise.resolve(undefined)
            : Promise.all(this.arr.map((ea) => ea.promise)).then(() => undefined);
    }
}
exports.Mutex = Mutex;
//# sourceMappingURL=Mutex.js.map