AngularJs promises: can you use both success and then? - javascript

I feel this is a very bad idea as i do not find it anywhere but testing it on my localhost it seems to work. I have a service :
angular
.module('trips.services')
.service('Trip', Trip);
function Trip($http, $q) {
//.....
function create(data) {
var api_url = "/api/trip";
return $http.post(api_url, data).success(function(data) {
mixpanel.track("Trip: created", {}); // track that a new event happened
});
}
}
I also have a controller where i do something like:
Trip.create(data).success(function(data) {
//do something after the trip is created
})
Now as you can see in this example I use both the then promise and the success callback in the service. If i do breakpoints in the debugger the success callback from the service is executed first and then the then clause from the controller. And it is also great for code organization, the common things i do after the service is successful is in the service while the specific things are in the controller. The thing is, i feel it is very non-according to any documentation or examples. Did not really find it somewhere, I discovered it by mistake. It might not work in some cases also?
I know the other options also, the $q library and could deffer in the service but that ends up in much more code.
So, is this a bad idea and why? Any opinions would be very helpful. I do not want to set up a wrong practice.

Using promises consequently is considered a good practice as you already know. To do that, you can simply replace your success-callback with another then:
function create(data) {
var api_url = "/api/trip";
var promise = $http.post(api_url, data);
promise.then(function(data) {
mixpanel.track("Trip: created", {}); // track that a new event happened
});
return promise;
}
Having multiple independent .then()s on one promise is perfectly fine and works as you would expect.

It's not necessarily a bad idea, it's just you using chaining of promises.
The .then function looks like this:
.then(function(success){}, function(error){});
you can see that .success is just shorthand for the first function above (as if you didn't declare the error callback).
Seeing as the promise can be passed around or maybe you have multiple services making the same request and you want just one promise created, then you might like to declare callbacks from multiple areas yet have just one promise resolve.
So is it a bad idea? No. Why? It is a flexible piece of code which is really up to the programmer to use. Can you declare multiple thens? Yes. Should you mix it with success callbacks? You can, but you might just want to stick to one style for consistency. But thats really up to what you're trying to achieve.
Angular documentation: https://docs.angularjs.org/api/ng/service/$http
(Watch out for deprecation, might just want to stick to .then)

Related

Q library (javascript) - handling q.reject() in a promise with Q.all()

I'm building a blog site w/Express, and using Q for the first time, and I was hoping to tap into the knowledge of veteran Q users.
I'm making one request to my DB to load post data, and another request that hits the Instagram API (unless it's already cached) and returns some json. So I have something like:
Q.all([blogPromise, instagramPromise]).then(good, bad);
The issue/question I'm running into is that say my request fails in my instagramPromise and I call deferred.reject(), my bad function is called. However, I still want to load the page with the blog post data if my blogPromise resolves, but it seems I'm not getting any arguments when my bad function is called (e.g. I don't get the blogPromise data that was successfully fetched).
Given this, it seems my only option is to not call deferred.reject() when I have an error, and instead call deferred.resolve() with something like deferred.resolve({error: true}) which I can then use in my good function to handle what gets passed to my view.
So my question is, does this sound right? Is this not a misuse of Q using resolve when in fact I'm running into an error and should be using reject? Or am I missing something with Q that would allow a better approach?
If you want your promise to resolve when both blogPromise and instagramPromise either resolves or rejects, you need to use allSettled method. Here is an example from the documentation:
Q.allSettled([blogPromise, instagramPromise])
.then(function (results) {
var loaded = results.filter(function (result) {
return result.state === "fulfilled";
});
good(loaded);
});
Inside of allSettled's then callback you can filter successfully loaded results and pass them to the good function. Or handle failed results somehow with bad one.
Something like this perhaps?
Q.all([
blogPromise,
instagramPromise.catch(function() { return {error: true}; })
]).then(good, bad);
It's similar to the approach you mention, with the difference that the error suppression is done in the place where it's used, rather than in the place where the error originates.

AngularJS - Do you return the promise or data from the service/factory?

In my controller, I use a method from a factory to update some data. For example, I'm trying to fetch an updated array of users. Should I be returning the promise itself from the factory? Or should I be returning the data from the promise (not sure if I phrased that correctly)?
I ask because I've seen it both ways, and some people say that the controller shouldn't have to handle whether the request was successful or if it failed. I'm specifically talking about the promise returned from $http in this case.
Or maybe in other words, should I be using the then() method inside the factory, or should I be using it in the controller after returning from the factory?
I've tried to handle the success and error callbacks (using the this() method) from within the service, but when I return the data to the controller, the users array is not properly updated. I'm assuming that's because of the request being async. So in the controller, it would look something like this:
vm.users = userFactory.getUsers();
If I handle the promise from within the controller, and set the users array within the then() method, it works fine. But this goes back to where I should be using then():
userFactory.getUsers().then(
function(data){
vm.users = data;
}, ...
Hopefully someone would be able to shed some light on this or provide some input. Thanks!
There's no way you can return the data from the factory (since it's an async call) without using either a callback approach (discouraged):
userFactory.prototype.getUsers = function(callback){
$http.get('users').then(function (response) {
callback(response.data);
});
};
Or the promise approach.
If you're worried about handling the errors on the controller, then worry not! You can handle errors on the service:
userFactory.prototype.getUsers = function(){
return $http.get('users').then(function(response) {
return response.data;
}, function(error) {
// Handle your error here
return [];
});
};
You can return the results of then and it will be chained. So things from service will execute and then, later on, Controller ones.
I have no problem with controller deciding what to do basing on response failing/succeding. In fact it lets you easily handle different cases and doesn't add a lot of overhead to the controller (controller should be as small as possible and focused on current task, but for me going different path whether request failed is the part of its task).
Anyway, in Angular HTTP requests are wrapped in promises internally (I remember that in the previous versions there was a way to automatically unwrap them), so returning from service/factory always returns a promise, which has to be resolved.
I prefer returning a promise from a service/factory because I tend to let other classes decide what to do with the response.

Understanding jQuery.Deferred in the context of jQuey.AJAX (again)

I concede that, despite hours of reading and attempting, I am fundamentally unable to grasp something about Deferred promises and asynchrony in general.
The goal on my end is real, real simple: send some data to the server, and react to the contents of the response conditionally.
The response will always be a JSON object with save and error keys:
{ "save": true, "error":false}
// or
{ "save" : false,
"error" : "The server has run off again; authorities have been notifed."}
I have tried dozens and dozens of variations from the jQuery API, from other stackexchange answers, from tutorials, etc.. The examples all seem concerned with local asynchronous activity. When I need is some ability to be made aware when the AJAX request has either finished and returned a response I can inspect and make decisions about, or else to know that it's failed. Below, I've used comments to explain what I think is happening so someone can show me where I'm failing.
I know this is a repost; I am, apprently, worse than on average at grasping this.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
/* when(), as I understand it, should fire an event to be
responded to by then() when it's contents have run their course */
var result = $.when(
/* here I believe I'm supposed to assert what must complete
before the when() event has fired and before any chained
functions are subsequently called */
/* this should return a jqXHR object to then(), which is,
I'd thought, a queue of functions to call, in order,
UPON COMPLETION of the asynchronous bit */
$.post("my/restful/url", postData))
.then( function() {
/* since "this" is the jqXHR object generated in the $.post()
call above, and since it's supposed to be completed by now,
it's data key should be populated by the server's response—right? */
return this.data;
});
// alas, it isn't
console.log(result.data);
// >> undefined
Most examples I can find discuss a timeout function; but this seems, as I understand, to be a failsafe put in place to arbitrarily decide when the asynchronous part is said to have failed, rather than a means of stalling for time so the request can complete. Indeed, if all we can do is just wait it out, how's that any different from a synchronous request?
I'll even take links to a new read-mes, tutorials, etc. if they cover the material in a different way, use something other than modified examples from the jQuery API, or otherwise help this drooling idiot through the asynchronous mirk; here's where I've been reading to date:
jQuery API: Deferred
JQuery Fundamentals
jQuery Deferreds promises asynchronous bliss (blog)
StackOverflow: timeout for function (jQuery)
Update
This is in response to #Kevin B below:
I tried this:
var moduleA = {
var moduleB = {
postData: {"id":7, "answer":"Ever since I went to Disneyland..."};
save: function() {
return $.post("path/to/service", postData, null, "JSON");
}
};
var result = this.moduleB.save();
result.done(function(resp) {
if (resp.saved == true) {
// never reached before completion
console.log("yahoo");
} else {
console.log("Error: " + resp.error);
// >> undefined
}
});
}
You are over-complicating your code. You cannot get the data to outside of the callback, no matter how many deferred/promises you create/use (your sample creates 3 different deferred objects!)
Use the done callback.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
$.post("my/restful/url", postData).done(function (result) {
console.log(result.save, result.error);
});
You seem to have a misunderstanding of both asynchronous requests, the Promise pattern, and Javascripts mechanism of passing functions as an argument.
To understand what's really happening in your code I suggest you use a debugger and set some breakpoints in the code. Or, alternatively, add some console.logs in your code. This way you can see the flow of the program and might understand it better. Also be sure to log the arguments of the function you pass as an argument in the then()-method, so you understand what is passed.
ok you got it half right. the problem is that when you execute the console.log the promised is not yet fulfilled the asynchronous nature of the promises allows the code to execute before that ajax operation is done. also result is a deferred not a value, you need to handle your promised with .done instead of .then if you wish to return a value otherwise you'll continue passing promises.
so that said
var result={};
$.when(
$.post("my/restful/url", postData))
.done( function(data) {
result.data=data;
});
// here result is an object and data is a undefined since the promised has no yet been resolve.
console.log(result.data);

readTextAsync in winjs does not always read my file

My question is about readTextAsync and writeTextAsync in the context of windows store applications. I have searched StackOverflow and MSDN and also otherwise Googled extensively.
My code is given below:
Windows.Storage.ApplicationData.current.roamingFolder.getFileAsync("sample.txt")
.then(
function(samplefile){
return Windows.Storage.FileIO.readTextAsync(samplefile)
},
function(e){},
function(samplefile){
Windows.Storage.FileIO.readTextAsync(samplefile)
}
)
.done(
function(something){ data = something; },
function(){},
function(something){ data = something; }
);
My problem is that most of the time the file does not get read. When I debug, it gets read intermittently.
It appears to be an issue of not allowing enough time for the async call to complete.
I am totally new to Windows app programming and javascript.
I would appreciate any help. Thanks. ravi
Whan you chain many promises you should have one error function at the end where you put your "done".
In this way you will be able to see if there is an error while it is reading.
The way you should write it is:
Windows.Storage.ApplicationData.current.roamingFolder.getFileAsync("sample.txt")
.then(
function(samplefile){
return Windows.Storage.FileIO.readTextAsync(samplefile)
}
)
.done(
function(data){ //do something with your data, like assign to a list },
function(error){ //do something with error },
function(data){ //do something with your data } //progress function,not sure what you want to do with this
);
But this call may not be your problem, if you put this code in a normal function and then call it, you will not be able to see the data object because it will be loaded in an asynchronous way.
You have to process your data inside the done function because if you assing it to an external variable (your data object) like you did, that variable will be empty when you try to use because most likely the done method hasn't occured yet.
In the progress handler for then, I am just trying to repeat the call, to ensure completion.
That makes no sense. It might even lead to race conditions, since you try to read the file while getting it is still in progress. Also, the repeated call does not return anything, and the task will not be chained into / synchronized with the flow of the rest. Remove that handler.
The reason for the emptry error handler is I haven't decided what to do in case of an error.
You can just omit it then. Errors would then just "bubble" up, and can be caught in other promises that depend on the failed one.
I want the text that is read to be stored in data for processing later.
But when is "later"? You would need to ensure that the processing does not start before the file is completely read - for which you will need to hook a done handler on your promise. Do the processing in the promise chain as well, never use global/higher-scoped variables with promises. If you need data multiple times, you can simple store the promise in a variable, and attach multiple done handlers (which will work even when the promise is already resolved).

Returning angular promise from a function that resolves the promise very quickly

I am writing an asynchronous javascript function that will be called by consumers to get certain data. Following is the simple implementation that I wrote initially (error handing and other stuff removed for clarity).
function getData(callback){
if (data is available as a JavaScript object){
callback(data);
}else{
getAsyncData(function(data){
//some transformations on data
callback(data);
});
}
}
What is important to note is that getData can return data quickly if data is already available as a JavaScript object.
I want to replace this implementation with the one that returns a promise object to the caller. This fiddle shows sample implementation - http://fiddle.jshell.net/ZjUg3/44/
The question - Since getData can return quickly, can there be a possiblity where getData is resolving the promise even before caller has established handler chain using then method? Just to simulate this, in the fiddle if i call then method inside setTimeout function (with zero delay), callback doesn't get called. If i call the then method outside of the setTimeout function, callback gets called. I am not sure if this is even a valid concern or valid usecase. I am quite new to angularjs development and would appreciate your views :)
If you want getData() to return a $q promise instead of using a callback, I'd do the following refactor using $q.when() and usual $q.resolve():
function getData()
{
if (data is available as a JavaScript object) {
return $q.when(data); // resolves immediately
} else {
var q = $q.defer();
getAsyncData(function(data){
//some transformations on data
q.resolve(data);
});
return q.promise;
}
}
No, a significant and important part of being a promise is that it doesn't matter when you attach the handler. Even if you create a promise now and resolve it immediately, then keep your computer running for the next 50 years, then attach a handler it will still fire.
All of this does assume that there isn't a bug/corner case in angularjs's promise implementation. If it doesn't work, it's a bug though.
If you ever need to know anything about how promises work, you can always refer to the Promises/A+ spec which angular adheers to. As a spec, it's one of the simplest and easiest to understand that I've come across (although I should mention that I've been involved in the spec for quite a while now).

Categories

Resources