Promises with AngularJS

This fiddle illustrates how we can use the native Promise with AngularJS. The Promise knows how to trigger only the minimum required number of digest cycles.

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <title>Document</title>
  </head>

  <body>
    <div ng-app="app">
      <div ng-controller="MainCtrl as $ctrl">
        <p>asyncValue: <strong>{{ $ctrl.asyncValue }}</strong></p>
        <p>classicPromiseResolve: <strong>{{ $ctrl.classicPromiseResolve }}</strong></p>
        <p>promisesRace: <strong>{{ $ctrl.promisesRace }}</strong></p>
      </div>
    </div>
  </body>

</html>

JavaScript

angular
      .module('app', [])
      // At run time, we overwrite the native Promise using Proxy
      // IDEA FOR IMPROVEMENT: using angular.injector(['ng']).get('$rootScope') we can get the $rootScope even before AngularJS has bootstraped.
      .run(function($rootScope) {
        function triggerDigestIfNeeded() {
          // $applyAsync acts as a debounced funciton which is exactly what we need in this case to get the minimum of digest cycles fired.
          $rootScope.$applyAsync();
        };

        // This principle can be used with other native JS "features" when we want to integrate then with AngularJS; for example, fetch.
        Promise = new Proxy(Promise, {
          // We are interested only in the constructor function
          construct(target, argumentsList) {
            return (() => {
              const promise = new target(...argumentsList);

              // The first thing a promise does when it gets resolved or rejected, is to trigger a digest cycle, if needed
              promise.then((value) => {
                triggerDigestIfNeeded();

                return value;
              }, (reason) => {
                triggerDigestIfNeeded();

                return reason;
              });

              return promise;
            })();
          }
        });
      })
      // This outputs the number of digest cycles
      .run((() => {
        let digestCycles = 0;

        return function($rootScope) {
          $rootScope.$watch(() => console.log(++digestCycles))
        }
      })())
      // Here we are testing the Promise and async/await features
      .controller('MainCtrl', function() {
        this.asyncValue = 'unresolved';
        this.classicPromiseResolve = 'unresolved';
        this.promisesRace = 'unresolved';

        this.$onInit = () => {
          this.testAsyncFunction();
          this.testClassicPromiseResolve();
          this.testPromisesRace();
        };

        this.testClassicPromiseResolve = () => {
 ...