Promise chaining when using $timeout

twip picture twip · Mar 8, 2016 · Viewed 13.5k times · Source

I'm trying to understand the promise API and chaining, particularly the timing when $timeoutis used with .then(). What I had expected from the following is that since $timeout returns a promise, .then() would not be called until it had resolved.

But instead of ABAB, it's ABBA all the time.

How can I use the promise API to ensure that a long-running call (or delayed call using $timeout) is actually complete before the .then() gets executed?

Code

angular
  .module('app', [])
  .controller('ThenCtrl', ThenCtrl);

function ThenCtrl($timeout, $q) {
  var vm = this;

  vm.items = [];

  $q.when(pushA()).then(pushB());

  $timeout(pushA, 5000).then(pushB());

  function pushA() {
    vm.items.push('A');
  }

  function pushB() {
    vm.items.push('B');
  }
}

Markup

<div ng-app="app">
  <div ng-controller="ThenCtrl as vm">
    {{vm.items}}
  </div>
</div>

I've set up a fiddle: https://jsfiddle.net/kan3c61t/

Answer

georgeawg picture georgeawg · Mar 8, 2016

Don't invoke the functions inside the .then methods.

  ̶$̶q̶.̶w̶h̶e̶n̶(̶p̶u̶s̶h̶A̶(̶)̶)̶.̶t̶h̶e̶n̶(̶p̶u̶s̶h̶B̶(̶)̶)̶;̶
  $q.when(pushA()).then(pushB);

  ̶$̶t̶i̶m̶e̶o̶u̶t̶(̶p̶u̶s̶h̶A̶,̶ ̶5̶0̶0̶0̶)̶.̶t̶h̶e̶n̶(̶p̶u̶s̶h̶B̶(̶)̶)̶;̶    
  $timeout(pushA, 5000).then(pushB);

Instead pass the functions as arguments to the .then method. The $q service will hold those functions to be invoked later.

The way the $q service works is it stores the argument of the .then method as a function to be invoked later. In this case, the $q service was storing the value returned by pushB() with the side effect of pushing B immediately onto the array.

The DEMO on JSFiddle