Home Reference Source

js-junk-bucket-repo/future.js

/*
 * Provides facilities for resolution of work in the future
 */

/**
 * A synchronization point for resolving a value at some point in the future through discontinuous flows.  For example,
 * an RPC request to a remote service.
 */
class Future {
	constructor() {
		this.resolved = false
		this.promised = new Promise( (accept, reject) => {
			if( this.resolved ){
				if( this.accept ){
					accept( this.value )
				} else if( this.reject ){
					reject( this.value )
				} else {
					throw new Error("Resolved but neither accepted or rejected.")
				}
				this.value = undefined
			} else {
				this.acceptPromise = accept
				this.rejectPromise = reject
			}
		})
	}

	reject( value ){
		if( this.resolved ){ throw new Error("Already resolved") }

		this.resolved = true

		if( this.rejectPromise ){
			this.rejectPromise( value )

			this._resolve()
		} else {
			this.reject = true
			this.value = value
		}
	}

	accept( value ){
		if( this.resolved ){ throw new Error("Already resolved") }

		this.resolved = true

		if( this.acceptPromise ){
			this.acceptPromise( value )

			this._resolve()
		} else {
			this.accept = true
			this.value = value
		}
	}

	_resolve() {
		this.acceptPromise = undefined
		this.rejectPromise = undefined
	}
}

function delay( forMilliseconds, value ) {
	const future = new Future();
	setTimeout( () => {
		future.accept( value )
	}, forMilliseconds );
	return future.promised;
}

function promiseEvent( from, name ){
	const future = new Future();
	from.once(name, (event) => {
		future.accept(event);
	});
	from.once('error', (why) => {
		future.reject(why);
	});
	return future.promised;
}

async function parallel( promises ){
	async function resolvePromise( p ){
		try {
			const value = await p;
			return {ok: true, value};
		}catch(problem){
			return {ok:false, problem};
		}
	}

	const resolutions = promises.map( ( p ) => {
		return resolvePromise(p);
	});
	const results = await Promise.all( resolutions );
	const out = results.reduce( ( output, result ) => {
		if( result.ok ){
			output.good.push(result.value);
		} else {
			output.bad.push(result.problem);
		}
		return output;
	}, { good: [], bad: [] });

	if( out.bad.length > 0 ){
		//TODO: Figure out better way to dsiplay the multiple errors.
		const error = new Error("Failed to resolves all promises");
		error.good = out.good;
		error.bad = out.bad;
		throw error;
	}
	return out.good;
}

module.exports = Future
module.exports.delay = delay;
module.exports.promiseEvent = promiseEvent;
module.exports.parallel = parallel;