Source: sg2d-deferred.js

"use strict";

/**
 * Промис с возможностью вызывать resolve/reject извне
 * @typedef SG2D.Deferred.Promise
 * @type {object}
 * @property {function} resolve(value) - Завершает промис с успехом
 * @property {function} reject(error) - Завершает промис с ошибкой
 * @property {function} then(callback) - Добавляет колбэк в список вызовов при успешном завершении промиса
 * @property {function} catch(callback) - Добавляет колбэк в список вызовов при аварийном завершении промиса
 * @property {function} isCompleted - Возвращает статус промиса (**true** для успешно завершенного промиса, **false** для не завершенного промиса или завершенного аварийно)
 */

/**
 * Создаёт экземпляр {@link SG2D.Deferred.Promise}
 * @class SG2D.Deferred
 * @return {SG2D.Deferred.Promise}
 */
function SG2DDeferred() {
	let thens = [];
	let catches = [];

	let status;
	let resolvedValue;
	let rejectedError;

	return {
		isCompleted: ()=>{
			return status === 'resolved';
		},
		resolve: value => {
			status = 'resolved';
			resolvedValue = value;
			thens.forEach(t => t(value));
			thens.length = 0; // Avoid memleaks.
		},
		reject: error => {
			status = 'rejected';
			rejectedError = error;
			catches.forEach(c => c(error));
			catches.length = 0; // Avoid memleaks.
		},
		then: cb => {
			if (status === 'resolved') {
				cb(resolvedValue);
			} else {
				thens.unshift(cb);
			}
		},
		catch: cb => {
			if (status === 'rejected') {
				cb(rejectedError);
			} else {
				catches.unshift(cb);
			}
		}
	}
}

export default SG2DDeferred;