监视promise's然后error's的功能,破坏对promise的引用

Spying on promise's then error's function, breaks reference to promise

本文关键字:promise 引用 error 然后 监视 功能      更新时间:2023-09-26

找到解决方案

Promises:转到下一个错误函数只需要重新抛出错误。

为什么promise的引用在监视它时不执行then的error函数?

有两个按钮返回(调用)相同的承诺,一个执行ok ($scope.expected() -按钮呈现红色),另一个进入成功函数($scope.notExpected() -按钮呈现绿色),这不是预期的行为。

当前的解决方案是,在另一个承诺中包装;

var app = angular.module('thenApp', []);
function ThenCtrl($scope, thenService, $q) {
  $scope.expected = function() {
    return thenService.doIt()
      .then(function() {
        console.log('was a success');
      });
  };
  $scope.notExpected = function() {
    return thenService.doIt()
      .then(function() {
        console.log('was a success');
      }, function() {
        console.log('has failed');
      });
  };
  $scope.solution = function() {
    var deferred = $q.defer();
    thenService.doIt()
      .then(function() {
        deferred.resolve();
        console.log('was a success');
      }, function() {
        deferred.reject();
        console.log('has failed');
      });
    return deferred.promise;
  };
}
app.controller('ThenCtrl', ThenCtrl);
var CLICK_EVENT = 'click';
var TIMEOUT_TO_END_ANIMATION = 1000;
var SUCCESS_CLASS = 'btn-success success';
var FAIL_CLASS = 'btn-error error';
var LOADING_CLASS = 'loading';
/**
 * Inspiration from https://github.com/johannesjo/angular-promise-buttons
 * @param $parse
 * @param $timeout
 * @returns {{scope: {promise: string, stateRedirect: string}, link: link}}
 */
function btnLoader($parse, $timeout) {
  return {
    restrict: 'A',
    require: '?ngClick',
    scope: true,
    link: function(scope, el, attrs) {
      el.addClass('btn-load');
      var promiseWatcher;
      // we need to use evalAsync here, as
      // otherwise the click or submit event
      // won't be ready to be replaced
      scope.$evalAsync(function() {
        var cb = $parse(attrs.ngClick);
        function buttonLoader() {
          // Make sure we run the $digest cycle
          scope.$apply(function() {
            var promise = cb(scope.$parent, {
              $event: CLICK_EVENT
            });
            // only init watcher if not done before
            if (!promiseWatcher) {
              // watch promise to resolve or fail
              promiseWatcher = scope.$watch(function() {
                return promise;
              }, function(nVal) {
                // for regular promises
                if (nVal && nVal.then) {
                  el.unbind(CLICK_EVENT);
                  el.addClass(LOADING_CLASS);
                  el.removeClass(SUCCESS_CLASS);
                  el.removeClass(FAIL_CLASS);
                  nVal.then(function() {
                    // promise was a success
                    el.addClass(SUCCESS_CLASS);
                    if (attrs.alwaysBind) {
                      el.bind(CLICK_EVENT, buttonLoader);
                    }
                  }, function() {
                    // promise was a fail
                    el.addClass(FAIL_CLASS);
                    el.bind(CLICK_EVENT, buttonLoader);
                  }).finally(function() {
                    el.removeClass(LOADING_CLASS);
                    promiseWatcher();
                    promiseWatcher = null;
                    $timeout(function() {
                      el.removeClass(SUCCESS_CLASS);
                      el.removeClass(FAIL_CLASS);
                    }, TIMEOUT_TO_END_ANIMATION);
                  });
                }
              });
            }
          });
        }
        // unbind original click event
        el.unbind(CLICK_EVENT);
        // rebind, but this time watching it's return value
        el.bind(CLICK_EVENT, buttonLoader);
      });
    }
  };
}
app.directive('btnLoader', btnLoader);
function thenService($q, $timeout) {
  thenService.doIt = function() {
    var deferred = $q.defer();
    $timeout(function() {
      deferred.reject(null);
    }, 500);
    return deferred.promise;
  };
  return thenService;
}
app.factory('thenService', thenService);
.loading {
  background: yellow !important;
}
.btn-error {
  background: red !important;
}
.btn-success {
  background: green !important;
}
.btn {
  margin-bottom: 20px;
  border: 1px solid rgba(0, 0, 0, 0.3);
  background: white;
  width: 250px;
  padding: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-transform: uppercase;
}
.btn:hover {
  background: lightgray;
  cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="thenApp" ng-controller="ThenCtrl">
  <p>
    Button action does not spy on then's error function.
  </p>
  <div class="btn" btn-loader="" always-bind="true" ng-click="expected()">
    As expected
  </div>
  <p>
    Button action spies on then's error function.
  </p>
  <div class="btn" btn-loader="" always-bind="true" ng-click="notExpected()">
    Not expected
  </div>
  <p>
    Solution for spying on then's error function.
  </p>
  <div class="btn" btn-loader="" always-bind="true" ng-click="solution()">
    Solution
  </div>
</div>

需要重新抛出错误。

someService.call().then(function(){
  // success
}, function(e){
  // error
  throw e;
})

承诺:转到下一个错误函数