ui-router resolve behaves strangely in Ionic

2019-01-29 01:09发布

问题:

I started from a demo Ionic app (ionic start myApp sidemenu), and added a resolve to one of the views:

resolve: {
  issue: function($q, $timeout) {
    var defer = $q.defer();
    //defer.reject();       // Doesn't work browser or device
    $timeout(defer.reject); // Works in browser, but not device
    return defer.promise;
  }
}

I monitor rejected resolves here:

.run(function($ionicPlatform, $rootScope, $ionicLoading) {
  $ionicPlatform.ready(function() {
    // regular stuff here

    $rootScope.$on('$stateChangeError', function() {
      $ionicLoading.show({
        template: 'All good!'
      });
    });
  });
});

For some reason, if resolve rejects immediately (see defer.reject() above), the callback of $stateChangeError is not run. If I do exactly the same, but outside of Ionic, it works!

Moreover, trying to delay the resolve rejection by doing $timeout(defer.reject); results in a different behaviour. Now it works in a browser as expected, but still doesn't work on a device. Trying to delay even more, results in success on device:

$timeout(function() {
  defer.reject();
}, 250); // Doesn't work for me with 200 or less

Can anyone shed light on this?

SEE HERE HOW TO REPRODUCE THE ISSUE

回答1:

From my experience with Angular and the promise model. In order to resolve/reject a promise Angular must tick over one cycle of the JS event loop - nextTick - and this can be accomplished using $scope.apply() which is how we mock such things in unit tests.

This is an awesome article that talks about $timeout and $scope.$evalAsync - from what I can gather $timeout is evaluating the function in the next tick. Which is the reason that this code works the way you outlined.

resolve: {
  issue: function($q, $timeout) {
    var defer = $q.defer();
    //defer.reject();       // <---- no nextTick
    $timeout(defer.reject); // <---- $timeout evaluates on nextTick
    return defer.promise;
  }
}

This is another article that talks about the textTick implementation of $q.

I know that this doesnt solve your problem - but it should shed some light onto why this is happening! Good luck!



回答2:

1) $timeout needs to be more than 200

First off - I have never seen constructs like $timeout(deferred.reject); I don't believe defered.reject returns any function that $timeout expects.

I tried 50 and it works. Here is http://plnkr.co/edit/6NXZEXrfz3WHVqoaRYJB?p=preview with timeout 50 or less but works i.e. All Good! is printed.

resolve: {
              test: function($q,  $timeout) {
                var defer = $q.defer(); 
                $timeout(function(){
                  defer.reject();
                },50);
                return defer.promise; 
              }
}

This is with stateChangeError outside the iconicPlatform.ready

2) $stateChangeError not working

I am thinking that this has to do with the timings. I don;t know the internals as to when the ionicPlatform.ready() gets executed but I am not sure if it is even necessary. Your starter module depends on it so it will be loaded. Sometimes seed projects have more than 'seed'.

UPDATED - Found a bug with respect to ready() on other devices. It seems that ready method is not getting executed.

https://github.com/driftyco/ionic/issues/1751