I am a bit confused about this. I have two get calls inside a function. Once this complete function, that is the two get calls are done, only then is this function done with its work. how should I used $q to get this to work as I want it? This is what I have now:
function updateBlackList() {
$http.get("http://127.0.0.1:8000/blacklist/entries/vehicle").then(function (res){
console.log(res)
}).catch(function (err) {
console.log(err)
});
})
$http.get("http://127.0.0.1:8000/blacklist/entries/person").then(function (res){
console.log(res)
}).catch(function (err) {
console.log(err)
});
});
return $q.when();
}
Here withint another function I need to wait for the above fiunction to complete:
BlackListService.updateBlackList().then(function() {
addVersion(server_version).then(function () {
console.log("Blacklist update complete")
})
})
Its not doing it like I was suspecting it to do. The Blacklist complete console is called before the tw get request are done
You want to combine both promises in one with $q.all()
function updateBlackList() {
return $q.all([
$http.get("http://127.0.0.1:8000/blacklist/entries/vehicle")
.then(function (res){console.log(res)})
.catch(function (err) {console.log(err)}),
$http.get("http://127.0.0.1:8000/blacklist/entries/person")
.then(function (res){console.log(res)})
.catch(function (err) {console.log(err)});
]);
}
Also, for your second example, you can chain promises to have a better looking code:
BlackListService.updateBlackList()
.then(function() {
return addVersion(server_version);
})
.then(function () {
console.log("Blacklist update complete");
})
Use $q.all.
var VEHICLE_URL = "http://127.0.0.1:8000/blacklist/entries/vehicle";
var PERSON_URL = "http://127.0.0.1:8000/blacklist/entries/person";
function updateBlackList() {
var p1 = $http.get(VEHICLE_URL).then(whatever);
var p2 = $http.get(PERSON_URL).then(whatever);
return $q.all([p1, p2]);
}
updateBlackList()
.then(whateverElse);
Related
I'm making a get request to my server, I get the response and I store the value inside a $scope.productId
userService.get(true)
.then(function(res) {
$scope.productId = res.user.productid;
}
});
then I need to use this value in another get request to the api to get the product related to this id.
apiService.get('/product/' + ???)
.then(function(response) {
console.log(response)
})
.catch(function(response) {
});
I'm new to promises, so the objective is to get the value of the first request in to the second one!
use this
userService.get(true)
.then(function(res) {
$scope.productId = res.user.productid;
apiService.get('/product/' + ???)
.then(function(response) {
console.log(response)
})
.catch(function(response) {
});
}
});
I'm struggling with something that I think should be pretty simple.
I want to see how many items are returned from a query() but using length isn't working for me.
This my code
getItems = function () {
// retrieve the list
theList = Users.query();
theList.$promise.then(function (res) {
console.log("success");
})
.catch(function (req) {
console.log("error");
})
.finally(function () {
});
return theList;
}
$scope.users = getItems();
console.log($scope.users.length);
This is my $resource:
.factory('Users', function ($resource) {
return $resource('https://example.com/:id', { id: '#id' }, {
update: { method: 'PUT' }
});
})
The console shows 0 even if there are items in the list.
Any idea what I'm doing wrong?
Try
getItems = function () {
// retrieve the list
theList = Users.query();
theList.$promise.then(function (res) {
console.log("success");
$scope.users = res;
console.log($scope.users.length);
})
.catch(function (req) {
console.log("error");
});
}
getItems();
Your getItem() is returning promise. So extract list from it and then try the length just you did inside your getItem();
//try this
getItems().then(function(result) {
$scope.users = result;
console.log('result: %o', $scope.users.length);
})
The past view days I read a lot of best practices in handling with promises. One central point of the most postings where something like this:
So if you are writing that word [deferred] in your code
[...], you are doing something wrong.1
During experimenting with the error handling I saw an for me unexpected behavior. When I chain the promises and It run into the first catch block the second promise gets resolved and not rejected.
Questions
Is this a normal behavior in other libs / standards (e.g. q, es6), too and a caught error counts as solved like in try / catch?
How to reject the promise in the catch block so that the second gets, called with the same error / response object?
Example
In this example you see 'I am here but It was an error'
Full Plunker
function BaseService($http, $q) {
this.$http = $http;
this.$q = $q;
}
BaseService.prototype.doRequest = function doRequest() {
return this.$http({
method: 'GET',
url: 'not/exisint/url'
})
.then(function (response) {
// do some basic stuff
})
.catch(function(response) {
// do some baisc stuff e.g. hide spinner
});
}
function ChildService($http, $q) {
this.$http = $http;
this.$q = $q;
}
ChildService.prototype = Object.create(BaseService.prototype);
ChildService.prototype.specialRequest = function specialRequest() {
return this.doRequest()
.then(function (response) {
alert('I am here but It was an error');
})
.catch(function (response) {
// do some more specific stuff here and
// provide e.g. error message
alert('I am here but It was an error');
return response;
});
}
Workaround:
With this workaround you can solve this problem, but you have to create a new defer.
BaseService.prototype.doRequest = function doRequest() {
var dfd = this.$q.defer();
return this.$http({
method: 'GET',
url: 'not/exisint/url'
})
.then(function (response) {
// do some basic stuff
dfd.resolve(response);
})
.catch(function(response) {
// do some basic stuff e.g. hide spinner
dfd.reject(error);
});
}
Your workaround is almost correct, you can simplify it to the following:
BaseService.prototype.doRequest = function doRequest() {
return this.$http({
method: 'GET',
url: 'not/exisint/url'
})
.then(function (response) {
// do some basic stuff
return response;
}, function (error) {
return this.$q.reject(error);
});
}
$q.reject is a shortcut to create a deferred that immediately get's rejected.
Yes, this is default behaviour in other libraries as well. .then or .catch simply wraps the return value into a new promise. You can return a rejected promise to make the .catch chain work.
You can also do the opposite, for instance when you want to reject the promise in the success callback for whatever reason:
function getData() {
return this.$http.get(endpoint).then(result => {
// when result is invalid for whatever reason
if (result === invalid) {
return this.$q.reject(result);
}
return result;
}, err => this.$q.reject(err));
}
getData().then(result => {
// skipped
}, error => {
// called
});
See example above
Just to add to Dieterg's answer and to your workaround, you can also wrap the code into $q constructor:
BaseService.prototype.doRequest = function doRequest() {
return $q(function (resolve, reject) {
$http.get('not/exisint/url').then(function (response) { // success
/* do stuff */
resolve(response);
}, function (error) { // failure
/* do stuff */
reject(error);
});
});
};
So I have a simple example of using $q.all to batch $resource calls, what I want to know is why is my update handler never called?
I would have thought it would be called after each promise is successfully completed?
Only the result handler is called. What am I doing wrong?
Here is the code snippet:
var promises = [];
angular.forEach($scope.mappedData, function(item) {
var resource = new Resource(item);
promises.push(resource.$save());
});
$q.all(promises).then(
function(result) {
console.log('result', result);
},
function(error) {
console.log('error', error);
},
function(notify) {
console.log('notify', notify);
}
);
$q.all creates a new singular promise that once all the previous promises are complete will then continue on. If you want to do each one individually you'll have to reference them individually.
I had the same problem, and I came with this solution. I've tried to arrange the code for your case.
var results = [], errors = [], d = $q.defer()
angular.forEach($scope.mappedData, function(item) {
var resource = new Resource(item);
resource.$save().promise
.then(function (result) {
results.push(result)
if(results.length == $scope.mappedData.length)
d.resolve(results)
}, function (err) {
errors.push(err)
results.push(null) // Very important =P
}, function (notf) {
d.notify(notf)
})
})
d.promise.then(function (results) {
console.log(results)
}, function (err) {
console.error(err)
}, function (notf) {
console.info(notf)
})
Let me know if it helps ;)
I have a chain of promises that are responsible for initializing my controller. In this chain if a certain condition isn't met, it would be best to send the user to another state via $state.go() and stop the rest of the promise chain from running. How can this be accomplished?
loadData1()
.then(function(){
return loadData2();
})
.then(function(){
if (...) {
$state.go(...); // how should the existing promise chain be killed off or stopped?
}
else {
return loadData3();
}
})
.then(function(){
return loadData4();
})
.then(function(){
console.log('controller initialized successfully');
},
function(error){
console.log('failed to initialize controller');
});
Instead of immediately calling $state.go, throw an error and check for it in the error handler at the end.
loadData1()
.then(function () {
return loadData2();
})
.then(function () {
if (exceptionalCondition) {
throw new Error('[MyCtrl:loadData2] Data failed to load!');
}
return loadData3();
})
...
.then(function () {
console.log('controller initialized successfully');
},
function (error) {
if (/^\[MyCtrl:loadData2\]/.test(error.message)) {
$state.go(redirect);
} else {
console.log('failed to initialize controller');
}
});
The nice thing about using promises is that they will handle errors and immediately terminate the chain if one occurs.