Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$http promise chain running in wrong order

I am new to angularjs. My goal is very simple. I want to make an ajax call to get data and, once complete, I want to make a second call to get another set of data that is dependent on information in the first set.

I'm trying to do this utilizing promise mechanisms so that I can utilize chaining instead of nested ajax calls and to better retain the ability to have independent functions that I can tie together as needed.

My code resembles the following:

var promiseGetWorkTypes = function ($q, $scope, $http) {
	console.log("promiseGetWorkTypes");

	return $q(function (resolve, reject) {
		$http({
			method: 'GET',
			url: '/WorkTypes'
		}).then(
			function (payload) {
				console.log("Got workttypegroups")
				console.log(payload);

				$scope.WorkTypeGroups = payload.data;

				console.log("End of worktypegroups");
				resolve(payload);
			},
			function (payload) {
				reject(payload);
			});
	});
};

var promiseGetRecentActivities = function ($q, $scope, $http) {
	console.log("promiseGetRecentActivities");

	return $q(function (resolve, reject) {
		$http({
			method: 'GET',
			url: '/RecentHistory'
		}).then(
			function (payload) {
				$scope.RecentActivities = payload.data;

				resolve(payload);
			},
			// data contains the response
			// status is the HTTP status
			// headers is the header getter function
			// config is the object that was used to create the HTTP request
			function (payload) {
				reject(payload);
			});
	});
};

var index = angular.module("index", []);

index
.controller('EntitiesController', function ($scope, $http, $timeout, $q) {
	promiseGetWorkTypes($q, $http, $scope)
		.then(promiseGetRecentActivities($q, $http, $scope));
}

However, when I look at my debug console, I see that the call to "promiseGetRecentActivities" is beginning before the call the Ajax handling has occurred for "promiseGetWorkTypes".

What am I missing or doing wrong here?

like image 661
dshockey Avatar asked Feb 08 '15 03:02

dshockey


1 Answers

When you write

promiseGetWorkTypes($q, $http, $scope).then(promiseGetRecentActivities($q, $http, $scope));

the promiseGetActivites is called at the time this line is evaluated. You should be able to wrap the call to promiseGetActivities in another function to delay the call until the first promise has resolved to get the calls to run in sequence:

promiseGetWorkTypes($q, $http, $scope).then(function() {
  promiseGetRecentActivities($q, $http, $scope);
});

The reason isn't anything to do with what happens inside then, but due to Javascript syntax. The following:

myFunc1(myFunc2());

passes to myFunc1 the result of calling myFunc2(), and not a reference to the myFunc2 function. So logically, myFunc2 would have to run before myFunc1. If you wrote

myFunc1(myFunc2);

then myFunc1 will receive a reference to myFunc2, and so myFunc1 would run before myFunc2 (and in fact, myFunc2 will only run if somewhere inside myFunc1 there is code that calls it).

Defining a function inline/anonymously doesn't change this behaviour. To pass a result of an anonymous function to another function you can do the following

myFunc1((function() {
  return 'something';
})());

which will evaluate the anonymous function first, as its return value, 'something' will be passed to myFunc1. To pass a reference to an anonymous function you can do the following:

myFunc1(function() {
  return 'something';
});

and then it will be up to myFunc1 whether it will ever call the function passed to it.

Bringing it back to your question, your code:

promiseGetWorkTypes($q, $http, $scope).then(promiseGetRecentActivities($q, $http, $scope));

is passing the result of promiseGetRecentActivities($q, $http, $scope) to then, so it must run before then is run, and so certainly doesn't wait for the promise from promiseGetWorkTypes to be resolved. What you seem to want is to pass it a function that, when called, runs promiseGetRecentActivities($q, $http, $scope), which is what

promiseGetWorkTypes($q, $http, $scope).then(function() {
  promiseGetRecentActivities($q, $http, $scope);
});

does.

As a side-note, it seems a bit unusual/overcomplicated to be passing $q, $http etc around to various functions, but I think probably beyond the scope of this question to go through alternatives.

like image 174
Michal Charemza Avatar answered Oct 07 '22 20:10

Michal Charemza