So this may be trivial, but I am doing some proof of concept stuff and trying to reject a promise in the middle of a promise chain, but I am not getting the results I would expect.
app.controller('MainCtrl', function($scope, $q) {
var def = $q.defer();
def.promise
.then(testPromiseReject())
.then(
function(){
console.log("SUCCESS")
},
function(){
console.log("FAIL")
});
def.resolve();
function testPromiseReject(action)
{
return $q.reject()
}
});
I think I am creating a promise that I initially resolve, but in the first then I have a function that I am trying to reject the rest of the promise chain. The above code prints "SUCCESS" to the console. Why is it not rejecting the rest of the chain?
There's a problem with this line...
.then(testPromiseReject())
It just needs the ()s removed so it's not executed immediately...
.then(testPromiseReject)
Fiddle... http://jsfiddle.net/5LVEE/1/
Related
I found myself in a little callback hell and found it difficult to test.
I hope maybe some of you guys could help me to figure this out
I have 2 services and my problem is with the fooService.getModel function
app.service('fooService', function($q, barService){
return {
getModel: function(){
var deferred = $q.defer();
barService.get().then(function(response){
//my bareService mock allow me to reach up to this point
var result = {};
//some processing logic (if, else, etc)
console.log('resolve result') // I totally see this log
deferred.resolve(result); //this is what i want to test
});
return deferred.promise;
},
process: function(){
this.getModel().then(function(result){
if(result.success){
barService.post().then(function(){
//whatever i dont care
})
}
});
}
}
});
and barService which contain 2 methods(get and store) basically $http calls.
for bar service i dont have problems testing because logic is isolated
I'm just faking the $httpBackend calls, with different responses.
the problem that i found when i want to verify the result object being returned on getModel function.
my test is something like
it('should test the returning model', inject(function(fooService){
barService.get.and.returnValue({then: function(successCallback){
successCallback({status:204, data: {}});
}});
var result;
fooService.getModel().then(function(response){
console.log('unit test then') // never gets in here
result = response;
});
//or anything related to the result object
expect(result).toEqual(mockedModel);
expect(result.success).toBeTruthy()
})
what i have seen is that if my getModel function will execute the http call itself, i can evaluate the result in my unit test.
something like
getModel: function () {
var deferred = $q.defer();
$http.get(url).then(function (response) {
deferred.resolve(response);
})
.catch(function (response) {
deferred.reject(response);
});
return deferred.promise;
}
i would definetly love to extract the functionality of my barService to my fooService, but this is very likely to be rehusable that why i extracted to a separate service.
do you have any idea how can i fix my unit test to evaluate the result response?
i hope its clear
thanks for the help guys
nevermind found that i even it is a service, i need to do $scope.digest in my unit test.
thanks!
I want to create a service that returns a json
Or by request to to the server, or by checking if it exists already in: Window.content
But I don't want to get a promise from my Controller !
I want to get the json ready !
I have tried several times in several ways
I tried to use with then method to do the test in my Service
but I still get a promise
( Whether with $http only, and whether with $q )
I could not get the value without getting promise from my Controller
My Service :
app.service('getContent',['$http', function( $http ){
return function(url){ // Getting utl
if(window.content){ // if it's the first loading, then there is a content here
var temp = window.content;
window.content = undefined;
return temp;
}
return $http.get(url);
};
}]);
My Controller:
.state('pages', {
url: '/:page',
templateProvider:['$templateRequest',
function($templateRequest){
return $templateRequest(BASE_URL + 'assets/angularTemplates/pages.html');
}],
controller: function($scope, $stateParams, getContent){
// Here I want to to get a json ready :
$scope.contentPage = getContent(BASE_URL + $stateParams.page + '?angular=pageName');
}
});
If the data exists, just resolve it in a promise.
While this process is still asynchronous it won't require a network call and returns quickly.
app.service('getContent',['$http', '$q', function( $http, $q ){
return function(url){
// create a deferred
var deferred = $q.defer();
if(window.content){ // if it's the first loading, then there is a content here
var temp = window.content;
window.content = undefined;
deferred.resolve(temp); // resolve the data
return deferred.promise; // return a promise
}
// if not, make a network call
return $http.get(url);
};
}]);
Just to reiterate, this asynchronous, but it won't require a network call.
This is not possible. If the code responsible to calculate or retrieve the value relies on a promise, you will not be able to return the value extracted from the promise by your function.
Explanation: This can easily be seen from the control flow. A promise is evaluated asynchronously. It may take several seconds to retrieve json from a server, but the caller of your function should not wait so long because your whole runtime environment would block. This is why you use promises in the first place. Promises are just a nice way to organize callbacks. So when your promise returns, the event that caused the function call will have already terminated. In fact it must have, otherwise your promise could not be evaluated.
You're thinking about this wrong. A service always returns a promise, because there is no synchronous way of getting JSON from an API:
app.factory('myService', ['$http', function($http) {
return $http('http://my_api.com/json', function(resp) {
return resp.data;
});
}]);
You would then call this within your controller like so:
app.controller('myController', ['$scope', 'myService', function($scope, myService) {
myService.then(function(data) {
$scope.contentPage = data; // here is your JSON
}, function(error) {
// Handle errors
});
}]);
Your service is returning a promise as it's written at the moment. A promise is always a promise, because you don't really know when it will be finished. However with Angular's 2 way data binding this isn't an issue. See my edits bellow as well as the example on $HTTP in the docs
In your controller
controller: function($scope, $stateParams, getContent){
getContent(BASE_URL + $stateParams.page + '?angular=pageName')
.then(aSuccessFn, aFailedFn);
function aSuccessFn(response) {
// work with data object, if the need to be accessed in your template, set you scope in the aSuccessFn.
$scope.contentPage = response.data;
}
function aFailedFn(data) {
// foo bar error handling.
}
}
This might be a nooby question but I still haven't been able to get my head around promises and specifically how to write code with them. (I've read several articles but most of them are abstract and I simply haven't written enough to have a clear picture)
I've got an AngujlarJS application that gets data through a http request to another server which sends a promise at first. I've been able to retrieve the response from the promise and use it in my app. However because my code is poorly written. It executes other code before the promise is resolved leading to problems. It starts loading the page before it has the data.
what i have is:
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object
//code that does someting with $scope.data
What i need is (I think)
var userTotals = *http request which returns a promise
$scope.data = userTotals.$object.
beforethisresolves(function{
show fancy loading icon or something })
.whenthis resolves(function{
//code that does someting with $scope.data
}
however I can't get the syntax correct.
This is what it looks like in general:
var promise = $http.post('/url');
console.log('Request started');
promise.then(function(result) {
console.log('Success');
console.log(result);
}, function() {
console.log('Failure');
});
In fact, $q AngularJS documentation helped me a good deal to get my head around promises concept.
Hope this helps!
var successCallback = function(){//...};
var errorCallback = function(){//...};
$http
.post('url', data)
.success(successCallback)
.error(errorCallback);
//OR
$http
.post('url', data)
.then(successCallback, errorCallback);
Assuming that you're using Bootstrap modal you can do the following:
function openModalWithProgressIndicator(deferred) {
const progressModal = $uibModal.open({
templateUrl: 'templates/modals/progress.html',
backdrop: 'static'
});
return deferred.then(function (value) {
return value;
}).finally(function () {
progressModal.close();
});
}
The deferred argument passed to this function is a promise. That said you can now do the following:
const deferred = $http.post('http://somewhere/data', {foo: 'bar'});
openModalWithProgressIndicator(deferred)
.then(function (httpResponse) {
// do sth with httpResponse.data
}).catch(function (error) {
// do sth with error
});
So the main point to note is the finally callback that's always executed.
I have the following q.all calling to resolve two promises. I checked all the posts and tried all other ways of implementation q.all and its the same case
var xyzdeffered = $q.defer();
service1.getServiceDetail1($routeParams.id).then(function(promise) {
xyzdeffered.resolve(promise);
});
var abcdeffered = $q.defer();
service2.getServiceDetail2($routeParams.id).then(function(promise) {
abcdeffered.resolve(promise);
});
$q.all([ xyzdeffered, abcdeffered ]).then(function(data) {
$scope.variable = data;
});
I am expecting the variable in q.all should get populated only after the first two promises are resolved. But unfortunately the service call itself doesn't get returned with data and the control moves on to the q.all. I find this weird because as per documentation the q.all is called only when your promises are returned with 200 response and are resolved.
I checked analysing the network calls and also put some alert to see the sequence of the code and find that the q.all alert is the first alert to be popped up and then the other promises are resolved.
Its really making me mad as why a simple implementation of q.all doesnt work..
Any help will be appreciated.
Why not directly call $q.all on first promise ?
$q.all([
service1.getServiceDetail1($routeParams.id),
service2.getServiceDetail2($routeParams.id)
]).then(function(data) {
//Array of result [resultOfgetServiceDetails1, resultOfgetServiceDetails2]
$scope.variable = data;
});
You need to reference the promise on the $q.defer() return object:
$q.all([ xyzdeffered.promise, abcdeffered.promise ])
For example you have you run multiple sq-lite queries synchronously just pass the array of queries(you can pass anything) as args into the call of this method.
function methodThatChainsPromises(args,tx) {
var deferred = $q.defer();
var chain = args.map(function(arg) {
var innerDeferred = $q.defer();
tx.executeSql(arg,[], function(){
console.log("Success Query");
innerDeferred.resolve(true);
}, function(){
console.log("Error Query");
innerDeferred.reject();
});
return innerDeferred.promise;
});
$q.all(chain).then(
function(results) {
deferred.resolve(true)
console.log("deffered resollve"+JSON.stringify(results));
}, function(errors) {
deferred.reject(errors);
console.log("deffered rejected");
});
return deferred.promise;
}
I have my application that should open a popup ask a confirmation at the user, then make an ajax cal and close the popup.
I tried to do it using a chain of promise (I've already used it, and I remember that it should work in this way), but it seems to block after the call toreservationService.confirm($scope.object);. Now it is a fake service implemented with a setTimeout and $q just to return a promise (in future it will make the ajax call). Is this a valid code or I didn't undestood how promise works?
For the popup I choose AngularUI and the code is this:
reservationService.book($scope.object, day)
.then(function(){
var dialogOpts = {/* dialog options omitted*/}
return $dialog.dialog(dialogOpts).open();
})
.then(function(result){
console.log('confirmed? ', result);
if (result){
//After this line it doesn't do nothing, also if the promise is resolved
return reservationService.confirm($scope.object);
}
})
.then(function(){
//this function is never executed
$scope.$emit('object:detail',{object: $scope.object});
});
reservationService:
function confirm(){
var deferred = $q.defer();
setTimeout(function(){
console.log('Confirming');
deferred.resolve(true)
}, 500);
return deferred.promise;
}
SOLVED
change setTimeout with $timeout angular's service
Used $timeout instead of setTimeout 'cause it works togheter at the angular scope, forcing the digest phase (or use $scope.apply() inside the setTimeout).
Can you try
//skipping the first then
.then(function(result){
var deferred = $q.defer();
console.log('confirmed? ', result);
if (result){
//After this line it doesn't do nothing, also if the promise is resolved
return deferred.resolve(reservationService.confirm($scope.object));
}
deferred.resolve();
return deferred.promise;
})
.then(function(){
//this function is never executed
$scope.$emit('object:detail',{object: $scope.object});
});
For chaining then, the last then success or failure function should return a promise. As the $q documentation mentions
then(successCallback, errorCallback) – regardless of when the promise
was or will be resolved or rejected, then calls one of the success or
error callbacks asynchronously as soon as the result is available. The
callbacks are called with a single argument: the result or rejection
reason.
This method returns a new promise which is resolved or rejected via
the return value of the successCallback or errorCallback.