"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
/* PROMISE_DEF */
const BluebirdPromise = require("bluebird");
/* END_PROMISE_DEF */
class OneTimePromise {
    /**
     * This is a Promise wrapper which allows the promise to be executed only once (any further execution returns prevoiusly loaded results)
     * @param timeout - timeout of waiting in milliseconds
     *
     */
    constructor(timeout) {
        this.started = false;
        this.finishedWithError = false;
        this.finishedWithResult = false;
        this.error = undefined;
        this.result = undefined;
        this.timeCounter = 0;
        this.timeout = timeout;
    }
    /**
     * Executes the promiseReturningFn only on the first call of execute
     * @param promiseReturningFn - Remember that when you call a function that returns a promise, the executor starts executing. That is why you have to pass an function here
     */
    execute(promiseReturningFn) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield new BluebirdPromise((resolve, reject) => {
                if (this.finishedWithError) {
                    setTimeout(() => reject(this.error), 4);
                }
                else if (this.finishedWithResult)
                    resolve(this.result);
                else if (!this.started || this.timeCounter > this.timeout) {
                    this.started = true;
                    return promiseReturningFn().then((result) => {
                        this.result = result;
                        this.finishedWithResult = true;
                        resolve(result);
                    })
                        .catch((error) => {
                        this.error = error;
                        this.finishedWithError = true;
                        setTimeout(() => reject(this.error), 4);
                    });
                }
                else {
                    this.awaitResult(resolve, reject);
                }
            });
        });
    }
    awaitResult(resolve, reject) {
        if (this.finishedWithError) {
            setTimeout(() => reject(this.error), 4);
        }
        else if (this.finishedWithResult)
            resolve(this.result);
        else {
            setTimeout(() => {
                this.timeCounter += 4;
                this.awaitResult(resolve, reject);
            }, 4);
        }
    }
}
exports.OneTimePromise = OneTimePromise;
//# sourceMappingURL=OneTimePromise.js.map