123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- // Import
- const typeChecker = require('typechecker')
- // Define
- module.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
- }
|