Rejecting promises with multiple arguments (e.g. $ http) in AngularJS

Callbacks for $http promises have several arguments: body, status, headers, config.

I would like to create a similar promise manually, but I donโ€™t know how to do it. What I would like to do is more or less:

 myservice.action().then(function(status, message, config) { // ... }); 

I know that I can pass an object with keys for a callback, but I would like to have a similar agreement as in $http . I am looking at angular sources, but either do not understand it completely, or simply cannot do it right.

Do you know how to create promises that can pass multiple arguments to callback / errbacks?

+6
javascript angularjs
Jul 16 '13 at 20:43
source share
1 answer

As pointed out in the comments, look at the $q implementation in AngularJS. Documents are notorious for being ... sometimes hard to understand.

But anyway, try a short example. I do this in CoffeeScript because I prefer it, but you should compile the coffeescript.org example if you want.

Let me implement the service:

 app = angular.module 'my.custom.services', ['your.other.modules'] app.factory 'Service', ['$http' , '$q', (http, q) -> # this is your deferred result deferred = q.defer() get: -> deferred.promise ] 

It is easy. Itโ€™s just a service that will use $q and $http , because: a) we want to get some of these sweet promises that we are talking about, and b) "$ http" is a good example in itself for what you can call asynchronous, and the result of which is not immediately available.

The interesting part here is the get part of the object, which returns here. Note that the service is implemented as a factory , not as a service . For differences see here . I usually think of it as a โ€œfancyโ€ version for a service, where I just have extra space for my own logic before exposing the service API (it really means something else, but it's for a different story.

In either case, get will return the promise deferred object when called. promise is an object that provides the then method. When using this service, you are likely to enter it like this:

 app = angular.module 'my.custom.application', ['my.custom.services'] app.controller 'AppController', ['Service', (service)-> service.get() # then what? ] 

As I said, get will simply return a promise. Promises, like in real life, should be allowed somewhere. So let's do it in the service - our promise will be fulfilled when we finish the task that we promised. It could be something like calling a URL via AJAX or a big computation (does anyone know what the 117th Fibonacci number is?).

In our example, we use the http call, as it is now, regardless of whether we return or not, and even when it returns to us:

 app.factory 'Service', ['$http' , '$q', (http, q) -> # this is your deferred result deferred = q.defer() # this is where http is used, this is started immediately, but takes a while http.get('some-resource.json').then (response) -> # now 'response' is the whole successful response, it has a data object with the payload if !someCondition deferred.resolve response.data #we have what we wanted else deferred.reject {error: "Failed", additional: "foo", number: 2} #we ran into some error get: -> deferred.promise ] 

Based on someCondition , we can resolve the request if we want. If you want to try it yourself, you can also use timeout , as in the docs.

What is happening now? Well, we still have this controller around:

 app.controller 'AppController', ['Service', (service)-> service.get().then(successCallback, errCallback) ] 

As I explained, promise provides a then method with a signature then(success, error) , where success and error are functions that take everything that we have allowed as arguments, for example:

 app.controller 'AppController', ['Service', (service)-> successCallback = (data) -> # we can work with the data from the earlier resolve here scope.data = data errCallback = (err) -> # the error object, we got from the rejection console.log err.error # => "Failed" service.get().then(successCallback, errCallback) ] 

If you want to pass multiple values โ€‹โ€‹to callbacks, I would suggest you pass the object when you allow / refuse a promise. You can also make callback calls to promise, for example angular makes an $http implementation in it:

 app.factory 'Service', ['$http' , '$q', (http, q) -> # this is your deferred result deferred = q.defer() # [...] get: -> promise = deferred.promise promise.success = (fn) -> promise.then (data) -> fn(data.payload, data.status, {additional: 42}) return promise promise.error = (fn) -> promise.then null, (err) -> fn(err) return promise return promise ] 

You significantly extend the promise given back by the success method, which takes one method as a callback, expects the promise to be resolved, and then uses the callback. You can do the same for any other method if you want (for tips see the angular implementation )

In your controller, you can use it as follows:

 service.get().success (arg1, arg2, arg3) -> # => arg1 is data.payload, arg2 is data.status, arg3 is the additional object service.get().error (err) -> # => err 

These are the basics, you can experiment, if you want, I suggest you try the following:

  • Multiple promises in a service with multiple pending promises
  • Try another method of deferring the result, for example, a long calculation

And, as a bonus:

  • Try isolating success / error callbacks in named service methods

This example uses the implementation of $q in Angular, you can, of course, use another implementation for Promises, for example this , which is the basis for $q .

+9
Jul 16 '13 at 21:27
source



All Articles