| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 | // Importconst typeChecker = require('typechecker')// Definemodule.exports = function ambi (method, ...args) {	// Prepare	let fireMethod, introspectMethod	// If binding has occured then make sure we are introspecting the write method	// by allowing the user to pass method as an array of two methods	// the method to fire, and the method to introspect	if ( typeChecker.isArray(method) ) {		[fireMethod, introspectMethod] = method	}	else {		fireMethod = introspectMethod = method	}	// Extract the preceeding arguments and the completion callback	const simpleArguments = args.slice(0, -1)	const completionCallback = args.slice(-1)[0]	// Check the completion callback is actually a function	if ( !typeChecker.isFunction(completionCallback) ) {		throw new Error('ambi was called without a completion callback')	}	/*	Different ways functions can be called:	ambi(function(a,next){return next()}, a, next)		> VALID: execute asynchronously		> given arguments are SAME as the accepted arguments		> method will be fired with (a, next)	ambi(function(a,next){return next()}, next)		> VALID: execute asynchronously		> given arguments are LESS than the accepted arguments		> method will be fired with (undefined, next)	ambi(function(a){}, a, next)		> VALID: execute synchronously		> given arguments are MORE than expected arguments		> method will be fired with (a)	ambi(function(a){}, next)		> INVALID: execute asynchronously		> given arguments are SAME as the accepted arguments		> method will be fired with (next)		> if they want to use optional args, the function must accept a completion callback	*/	const givenArgumentsLength = args.length	const acceptedArgumentsLength = introspectMethod.length	let argumentsDifferenceLength = null	let executeAsynchronously = null	// Given arguments are SAME as the expected arguments	// This will execute asynchronously	// Don't have to do anything with the arguments	if ( givenArgumentsLength === acceptedArgumentsLength ) {		executeAsynchronously = true	}	// Given arguments are LESS than the expected arguments	// This will execute asynchronously	// We will need to supplement any missing expected arguments with undefined	// to ensure the compeltion callback is in the right place in the arguments listing	else if ( givenArgumentsLength < acceptedArgumentsLength ) {		executeAsynchronously = true		argumentsDifferenceLength = acceptedArgumentsLength - givenArgumentsLength		args = simpleArguments.slice().concat(new Array(argumentsDifferenceLength)).concat([completionCallback])	}	// Given arguments are MORE than the expected arguments	// This will execute synchronously	// We should to trim off the completion callback from the arguments	// as the synchronous function won't care for it	// while this isn't essential	// it will provide some expectation for the user as to which mode their function was executed in	else {		executeAsynchronously = false		args = simpleArguments.slice()	}	// Execute with the exceptation that the method will fire the completion callback itself	if ( executeAsynchronously ) {		// Fire the method		fireMethod(...args)	}	// Execute with the expectation that we will need to fire the completion callback ourselves	// Always call the completion callback ourselves as the fire method does not make use of it	else {		// Fire the method and check for returned errors		const result = fireMethod(...args)		// Check the result for a returned error		if ( typeChecker.isError(result) ) {			// An error was returned so fire the completion callback with the error			const err = result			completionCallback(err)		}		else {			// Everything worked, so fire the completion callback without an error and with the result			completionCallback(null, result)		}	}	// Return nothing as we expect ambi to deal with synchronous and asynchronous methods	// so returning something will only work for synchronous methods	// and not asynchronous ones	// so returning anything would be inconsistent	return null}
 |