承诺链接和处理不承诺任何东西的行动

时间:2015-05-06 06:46:28

标签: angularjs angular-promise

我有一个关于Javascript承诺链接的问题。假设我在承诺链的某个地方有一个动作。该操作不会返回任何值,但必须在链继续之前完成。

我是否需要将此行动包含在承诺中?我需要这样的东西:

$q.when();

请参阅下面的代码:

...
var goToDashboard = function () {
    //TODO: use $q here?
    $state.go('dashboard');
};
...
activateEmail().then(signinByToken).then(setPersonalInfo).then(goToDashboard).then(somethingElse).catch(reportProblem);

有人可以提出建议吗?

2 个答案:

答案 0 :(得分:2)

在下文中,我演示了使用各种函数和其他返回类型的promise来链接.then。当然,在没有返回承诺的情况下,没有延迟解决,并且立即执行以下.then - 因此,如果您有需要完成的异步,则需要返回在异步任务完成时解析的承诺。请注意,返回$q.when()会返回一个承诺(包含您作为参数提供的任何内容),但它会立即解决。

另外,请注意$state.go actually returns a promise!因此,在上面的示例中,您只能return $state.go('dashboard');并且以下.then不应该执行,直到ui-router更改了路由(如下所示)。



(function() {
  "use strict";

  angular.module('myApp', ['ui.router', 'ngResource'])
    .controller('myController', ['$scope', '$state', '$q', '$timeout', '$resource', '$log', MyController])
    .config(['$stateProvider', configUiRouter]);

  function configUiRouter($stateProvider) {
    $stateProvider
      .state("home", {
        url: "/home",
        template: "<div>Home state</div>"
      })
      .state("dashboard", {
        url: "/dashboard",
        template: "<div>Dashboard state</div>"
      })
      .state("error", {
        url: "/error",
        template: "<div>Error state: I'm sorry Dave, I'm afraid I can't do that...</div>"
      });
  }

  function MyController($scope, $state, $q, $timeout, $resource, $log) {

    $scope.status = {
      emailActivated: false,
      signinByToken: false,
      personalInfo: false,
      stackoverflowUsers: null,
      loading: null,
      counter: 0
    };

    $state.go('home'); // set default state for ui-router test

    activateEmail()
      .then(updateStatusLoading).then(counting) // Loading: . Counter: 1
      .then(signinByToken)
      .then(updateStatusLoading).then(counting) // Loading: .. Counter: 2
      .then(setPersonalInfo)
      .then(updateStatusLoading).then(counting) // Loading: ... Counter: 3
      .then(goToDashboard)
      .then(updateStatusLoading).then(counting) // Loading: .... Counter: 4
      .then(somethingElse)
      .then(triggerError)
      .then(neverReached)
      .catch(catchesReject);


    /* * * * * * * * * * *
     * Promise functions *
     * * * * * * * * * * */

    // doesn't return any promise
    // (resolves immediately)
    function updateStatusLoading() {
      if (!$scope.status.loading) {
        $scope.status.loading = "";
      }
      $scope.status.loading += ".";
    }

    // returns something other than a promise (a String...)
    // (resolves immediately)
    function counting() {
      $scope.status.counter++;
      return "Did some counting... (" + $scope.status.counter + ")";
    }

    // using promise returned by $timeout
    // (delayed resolution)
    function activateEmail() {
      return $timeout(function simulateActivateEmailLatency() {
        $scope.status.emailActivated = true;
      }, 1000);
    }

    // using promise returned by $q.defer, resolved in a $timeout
    // (the return is immediate, but the resolve is delayed)
    function signinByToken() {
      var deferred = $q.defer();

      $timeout(function simulateSignInLatency() {
        $scope.status.signinByToken = true;
        deferred.resolve({
          returningSomething: "Is entirely optional"
        });
      }, 1000);

      //log to console what this object looks like
      $log.log("deferred.promise: ", deferred.promise);

      return deferred.promise;
    }

    // using promise created by $q.when; no timeout
    // (immediate resolution)
    function setPersonalInfo() {
      $scope.status.personalInfo = true;

      $log.log("$q.when: ", $q.when({
        foo: "bar"
      }));

      return $q.when({
        returningSomething: "Is entirely optional"
      });
    }

    // using promise created by $state.go
    // (will resolve once route has changed; which could include time spent doing ui-router resolves...)
    function goToDashboard() {
      // yup, this returns a promise!
      // https://github.com/angular-ui/ui-router/wiki/Quick-Reference#stategoto--toparams--options
      var goPromise = $state.go('dashboard');

      $log.log("$state.go: ", goPromise);

      return goPromise;
    }

    // using $promise returned by resource, and adding an .then
    // (resolves when the $resource does)
    function somethingElse() {
      var resourceContainingPromise = $resource('https://api.stackexchange.com/2.2/info')
        .get({
          site: 'stackoverflow'
        });

      // (note that it contains a $promise, is not a promise itself)
      $log.log("$resource: ", resourceContainingPromise);

      return resourceContainingPromise
        .$promise
        .then(function resourceHandler(results) {
          $scope.status.stackoverflowUsers = results.items[0].total_users;
        });
    }

    // returns a rejected promise
    // (immediate resolve)
    function triggerError() {
      var rejectPromise = $q.reject("An error message");

      $log.log("$q.reject: ", rejectPromise);

      return rejectPromise;
    }

    // this gets skipped due to .triggerError()
    function neverReached() {

      $log.error("Shouldn't see this!");

      $scope.status.loading += "Never reached!";
    }

    // this catches the $q.reject and logs the data it passed...
    function catchesReject(data) {
      $log.log(data); //log the error message
      return $state.go('error');
    }

  }
})();
&#13;
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular-resource.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.14/angular-ui-router.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="myController">
    <div style="display:inline-block; float:left; margin-right: 20px; min-width: 250px;">
      <ul>
        <li>Email activated: {{status.emailActivated}}</li>
        <li>Sign-in by Token: {{status.signinByToken}}</li>
        <li>Personal info set: {{status.personalInfo}}</li>
        <li>Stackoverflow Users: {{status.stackoverflowUsers}}</li>
      </ul>
      <hr />
      Loading: {{status.loading}}
      <br />Counter: {{status.counter}}
    </div>
    <div style="display:inline-block; padding: 10px; border: 1px solid grey; max-width: 150px;">
      <strong>Ui Router Test</strong>
      <div ui-view></div>
    </div>
  </div>
</div>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

我想我找到了问题的答案。

首先,需要考虑then()引用文档的事实,返回一个新的承诺,该承诺通过回调的返回值解决。

见下文:

  

然后(successCallback,errorCallback,notifyCallback) - 无论如何   当承诺已经或将要解决或拒绝时,请拨打一个   成功或错误回调一旦结果异步   是可用的。使用单个参数调用回调:   结果或拒绝原因。另外,通知回调可能是   在之前调用零次或多次以提供进度指示   承诺得到解决或拒绝。

     

此方法返回通过解析或拒绝的新承诺   successCallback的返回值,errorCallback(除非那个   value是一个promise,在这种情况下,它用值来解析   使用promise chaining解决了这个承诺。它也通知   通过notifyCallback方法的返回值。承诺不能   从notifyCallback方法中解析或拒绝。

所以我假设以下回调(确实明确地返回了一些内容)将只返回undefined本身包含在then()的承诺中:

var goToDashboard = function () {
    //TODO: use $q here?
    $state.go('dashboard');
};

所以我确实有一个承诺 - 感谢then() - 我不需要任何其他的东西......