I'm trying to understand the promise API and chaining, particularly the timing when $timeout
is 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/
Here you go.
What I did is essentially added a success
function in the then
part of the code.
$timeout(pushA, 5000).then(function(success) {
pushB()
});
Here is the working demo.
You can also add an error function
like this
$timeout(pushA, 5000).then(function(success) {
pushB()
},function(error){console.log("Error");});
While searching for this answer, I also came across this very helpful link
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
As others have mentioned - your bigger issue is that you .then(promise)
and not .then(function)
.
Promises represent a value + time. It's the result of an already started operation. A promise is a value - then
waits for functions. You can't "run a promise after another promise" - since a promise means the operation has already started.
When you then(x)
for anything other than a function it is ignored. This is an unfortunate choice in the promises spec but we have to live with it.
Since your calls are synchronous you should not use promises for it. If your code does something synchronous you can sequence actions with ;
and not then
:
pushA();
pushB();
If it's a call that returns promises then it just becomes:
pushA().then(pushB);
There is no point in calling $q.when
which converts non-promises to promises.
I'd write it as:
pushA();
$timeout(5000).then(pushB);
There is no point to convert the first synchronous action to a promise returning function or to involve promises anywhere except the timeout. If you need pushA
to happen after 5000ms itself I'd still probably write:
$timeout(5000).then(pushA).then(pushB)
Since I think it is more readable and again we don't involve pushA
and pushB
with promises directly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With