Promise a chain and work with an action that doesn't return anything along the chain of promises

I have a question regarding Javascript chaining. Say I have an action down the promise chain somewhere. This action does not return any value, but it must complete before the chain can continue.

Do I need to wrap this action in a promise? I need something like:

$q.when();

      

See my code below:

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

      

Anyone have any advice?

+3


source to share


2 answers


In the example below, I've demonstrated chaining .then

using promises from various functions and other return types. Without a promise return, there is of course no pending resolution and the next one .then

is executed immediately - so if you have something async to execute, you need to return a promise, which is resolved when the async task is complete. Note that the return $q.when()

returns a promise (wrapped around anything you specify as a parameter), but it resolved immediately.

Also note that the $state.go

promise actually returns
! So in the above example, you can just return $state.go('dashboard');

, and the next .then

one shouldn't be done until ui-router changes the route (shown below).



(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');
    }

  }
})();
      

<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>
      

Run codeHide result


+2


source


I think I have found the answer to my question.

First, one needs to consider the fact that then()

, quoting the documentation, returns a new promise that is resolved with the return value of the callback .

See below:

then (successCallback, errorCallback, notifyCallback) - regardless of when the promise was or will be resolved or rejected, then calls one success or failure callbacks asynchronously as soon as the result is available. Callbacks are invoked with one argument: reasons or rejection. Additionally, the notification callback can be zero or more times to provide an indication of progress until the promise is resolved or rejected.

This method returns a new promise that is resolved or rejected through the return value of successCallback, errorCallback (unless that value is a promise, in which case it is resolved with a value that resolves in this promise using the promise chain). It also notifies via the return value of the notifyCallback method. The promise cannot be resolved or rejected from the notifyCallback method.



So my guess is that the following callback (which explicitly returns something explicitly) will just return undefined

itself, wrapped in a promise then()

:

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

      

So I have a promise - thanks then()

- and I don't need anything else ...

0


source







All Articles