Ben Nadel: Monkey-Patching The $q Service With .fcall() In AngularJS

fcall() promises

by nickadeemus2002

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
<div ng-app="Demo" ng-controller="AppController">
    
<h1>Monkey-Patching The $q Service With .fcall() In AngularJS</h1>

    <p><em><storng>Note</strong>: This is not exactly the .fcall() method from Q. Rather, this is inspired by that concept.</em>
    </p>
</div>

CSS

a[ng-click] {
    cursor: pointer;
    text-decoration: underline;
}
p.apply, p.digest {
    background-color: #FAFAFA;
    border: 3px solid #CCCCCC;
    cursor: default;
    float: left;
    height: 75px;
    line-height: 75px;
    text-align: center;
    width: 200px;
}
p.apply {
    margin-right: 30px;
}
p.apply.hot, p.digest.hot {
    background-color: #FFCCCC;
    border-color: #FF3399;
}
p.logging {
    clear: both;
}

JavaScript

// Create an application module for our demo.
var app = angular.module("Demo", []);

// I monkey-patch the .fcall() method into the root of the $q service. We have
// to do this in a .run() block so that it will modify the $q service before any
// other component in the application needs it.
app.run(

function monkeyPatchQService($q, $exceptionHandler) {
    // I invoke the given function using the given arguments. If the
    // invocation is successful, it will result in a resolved promise; if it
    // throws an error, it will result in a rejected promise, passing the
    // error object through as the "reason."
    // --
    // The possible method signatures:
    // --
    // .fcall( methodReference )
    // .fcall( methodReference, argsArray )
    // .fcall( context, methodReference, argsArray )
    // .fcall( context, methodName, argsArrray )
    // .fcall( context, methodReference )
    // .fcall( context, methodName )
    $q.fcall = function () {
        try {

            var components = parseArguments(arguments);
            var context = components.context;
            var method = components.method;
            var inputs = components.inputs;

            return ($q.when(method.apply(context, inputs)));
        
        } catch (error) {

            // We want to pass the error off to the core exception handler.
            // But, we want to protect ourselves against any errors there.
            // While it is unlikely that this will error, if the app has
            // added an exception interceptor, it's possible something could
            // go wrong.
            try {
                
                $exceptionHandler(error);
                
            } catch (loggingError) {

                // Nothing we can do here.

            }

            return ($q.reject(error));

        }

    };


    // ---
    // PRIVATE METHODS.
    // ---


    // I parse the .fcall() arguments into a normalized structure that is
    // ready for...