Here's the code:
vm.saveData = function(data) {
demoService.saveData(data, function(response) {
if (response.status === 200) {
principal.identity(true).then(function() {
$state.reload();
return toastr.success('Success');
});
}
return toastr.error('Failure');
});
}
On getting success response from the api, it should display only the 'success' message. But, instead it displays the 'failure' message first and then the 'success' message. What am I doing wrong? Do I have to put a timeout or is there something which I'm missing here?
If the status is 200 then you set up a promise to call success later on.
Regardless of what the status is (because it is outside of the if and you haven't used an else) you always call error.
Presumably you just want to move return toastr.error('Failure'); into an else
That's not how you setup promises. Promises uses .then(). You are simply using passing the function in as a callback.
vm.saveData = function(data) {
demoService
.saveData(data)
.then(success, error);
function success(response) {
principal.identity(true).then(function() {
$state.reload();
return toastr.success('Success');
});
}
function error(response) {
return toastr.error('Failure');
}
};
Many systems such as AJAX send multiple messages to indicate progress in the task. You want to ignore the earlier messages. The failure message is from the earlier events while the action is incomplete.
I found out my mistake. Adding 'return' resolved the issue.
'return principal.identity(true).then(function(){
//do something here
});'
vm.saveData = function(data) {
demoService.saveData(data, function(response) {
if (response.status === 200) {
return principal.identity(true).then(function() {
$state.reload();
return toastr.success('Success');
});
}
return toastr.error('Failure');
});
}
I am putting catches at the end, but they are returning empty object in one particular instance at least. Is a catch necessary for anything unbeknownst, or is it just screwing me up?
$( document).ready(function(){
app.callAPI()//a chainable a RSVP wrapper around a jquery call, with its own success() fail() passing forward to the wrapper, so it will either be a resolved or rejected thenable to which is now going to be chained
.then(
function(env) {
//set the property you needed now
app.someSpecialEnvObj = env;
},
function(rejectMessage){
console.log('call.API() cant set some special env object..');
console.log(rejectMessage);
}
)
.catch(
function(rejectMessage){
if(rejectMessage){
//a just incase, DOES IT HAVE VALUE, for somebody that may have not done their homework in the parent calls?
console.log('you have some kind of legitimate error, maybe even in callAPI() that is not part of any problems inside them. you may have forgotton handle something at an early state, your so lucky this is here!)
} else {
console.log('can this, and or will this ever run. i.e., is there any value to it, when the necessity to already be logging is being handled in each and every then already, guaranteeing that we WONT be missing ANYTHING')
}
}
);
});
Is it wrong? or is there some kind of use for it, even when I still use an error/reject handler on all usages of .then(resolve, reject) methods in all parent chained then-ables?
EDIT: Better code example, I hope. I think I might be still be using some kind of anti-pattern in the naming, I rejectMessage in my e.g., it's the jqXhr object right?
So maybe I should be naming them exactly that or what? i.e. jqXhr? By the way, the reason I like to reject it on the spot inside each then(), if there was an error, is because this way I can copiously log each individual call, if there was a problem specifically there, that way I don't have to track anything down. Micro-logging, because I can.
Promises are helping opening up the world of debugging this way.
Here's the three examples I have tried. I prefer method1, and method2, and by no means am I going back to method3, which is where I started off in the promise land.
//method 1
app.rsvpAjax = function (){
var async,
promise = new window.RSVP.Promise(function(resolve, reject){
async = $.extend( true, {},app.ajax, {
success: function(returnData) {
resolve(returnData);
},
error: function(jqXhr, textStatus, errorThrown){
console.log('async error');
console.log({jqXhr: jqXhr, textStatus: textStatus, errorThrown: errorThrown});
reject({ jqXhr: jqXhr, textStatus: textStatus, errorThrown: errorThrown}); //source of promise catch data believe
}
});
$.ajax(async); //make the call using jquery's ajax, passing it our reconstructed object, each and every time
});
return promise;
};
app.callAPI = function () {
var promise =app.rsvpAjax();
if ( !app.ajax.url ) {
console.log("need ajax url");
promise.reject(); //throw (reject now)
}
return promise;
};
//method 2
app.ajaxPromise = function(){
var promise, url = app.ajax.url;
var coreObj = { //our XMLHttpRequestwrapper object
ajax : function (method, url, args) { // Method that performs the ajax request
promise = window.RSVP.Promise( function (resolve, reject) { // Creating a promise
var client = new XMLHttpRequest(), // Instantiates the XMLHttpRequest
uri = url;
uri = url;
if (args && (method === 'POST' || method === 'PUT')) {
uri += '?';
var argcount = 0;
for (var key in args) {
if (args.hasOwnProperty(key)) {
if (argcount++) {
uri += '&';
}
uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
}
}
}
client.open(method, uri);
client.send();
client.onload = function () {
if (this.status == 200) {
resolve(this.response); // Performs the function "resolve" when this.status is equal to 200
}
else {
reject(this.statusText); // Performs the function "reject" when this.status is different than 200
}
};
client.onerror = function () {
reject(this.statusText);
};
});
return promise; // Return the promise
}
};
// Adapter pattern
return {
'get' : function(args) {
return coreObj.ajax('GET', url, args);
},
'post' : function(args) {
return coreObj.ajax('POST', url, args);
},
'put' : function(args) {
return coreObj.ajax('PUT', url, args);
},
'delete' : function(args) {
return coreObj.ajax('DELETE', url, args);
}
};
};
app.callAPI = function () {
var async, callback;
async =app.ajaxPromise() ; //app.ajaxPromise() is what creates the RSVP PROMISE HERE<
if(app.ajax.type === 'GET'){async = async.get();}
else if(app.ajax.type === 'POST') {async = async.post();}
else if(app.ajax.type === 'PUT'){async = async.put();}
else if(app.ajax.type === 'DELETE'){ async = async.delete();}
callback = {
success: function (data) {
return JSON.parse(data);
},
error: function (reason) {
console.log('something went wrong here');
console.log(reason);
}
};
async = async.then(callback.success)
.catch(callback.error);
return async;
};
//method 3 using old school jquery deferreds
app.callAPI = function () {
//use $.Deferred instead of RSVP
async = $.ajax( app.ajax) //run the ajax call now
.then(
function (asyncData) { //call has been completed, do something now
return asyncData; //modify data if needed, then return, sweet success
},
function(rejectMessage) { //call failed miserably, log this thing
console.log('Unexpected error inside the callApi. There was a fail in the $.Deferred ajax call');
return rejectMessage;
}
);
return async;
};
I also run this somewhere onready as another backup.
window.RSVP.on('error', function(error) {
window.console.assert(false, error);
var response;
if(error.jqXhr){
response = error.jqXhr;
} else {
//response = error;
response = 'is this working yet?';
}
console.log('rsvp_on_error_report')
console.log(response);
});
Edit error examples
//one weird error I can't understand, an empty string("")?
{
"jqXhr": {
"responseText": {
"readyState": 0,
"responseText": "",
"status": 0,
"statusText": "error"
},
"statusText": "error",
"status": 0
},
"textStatus": "error",
"errorThrown": "\"\""
}
//another wierd one, but this one comes from a different stream, the RSVP.on('error') function
{
"readyState": 0,
"responseText": "",
"status": 0,
"statusText": "error"
}
I am putting catches at the end
That's the typical position for them - you handle all errors that were occurring somewhere in the chain. It's important not to forget to handle errors at all, and having a catch-all in the end is the recommended practise.
even if I use onreject handlers in all .then(…) calls?
That's a bit odd. Usually all errors are handled in a central location (the catch in the end), but of course if you want you can handle them anywhere and then continue with the chain.
Just make sure to understand the difference between an onreject handler in a then and in a catch, and you can use them freely. Still, the catch in the end is recommended to catch errors in the then callbacks themselves.
they are returning empty object in one particular instance atleast.
Then the promise screwed up - it should never reject without a reason. Seems to be caused be the
if ( !app.ajax.url ) {
console.log("need ajax url");
promise.reject();
}
in your code that should have been a
if (!app.ajax.url)
return Promise.reject("need ajax url");
Is a catch necessary for anything unbeknownst?
Not really. The problem is that catch is usually a catch-all, even catching unexpected exceptions. So if you can distinguish them, what would you do with the unexpected ones?
Usually you'd set up some kind of global unhandled rejection handler for those, so that you do not have to make sure to handle them manually at the end of every promise chain.
I think the general question deserves a simple answer without the example.
The pedantic technical answer is 'no', because .catch(e) is equivalent to .then(null, e).
However (and this is a big "however"), unless you actually pass in null, you'll be passing in something that can fail when it runs (like a coding error), and you'll need a subsequent catch to catch that since the sibling rejection handler by design wont catch it:
.then(onSuccess, onFailure); // onFailure wont catch onSuccess failing!!
If this is the tail of a chain, then (even coding) errors in onSuccess are swallowed up forever. So don't do that.
So the real answer is yes, unless you're returning the chain, in which case no.
All chains should be terminated, but if your code is only a part of a larger chain that the caller will add to after your call returns, then it is up to the caller to terminate it properly (unless your function is designed to never fail).
The rule I follow is: All chains must either be returned or terminated (with a catch).
If I remember correctly, catch will fire when your promise is rejected. Since you have the fail callback attached, your catch will not fire unless you call reject function in either your fail or success callback.
In other words, catch block is catching rejection in your then method.
Here is my code, it loops through forEach and prints out '1' but never returns from object.save() & never prints out 2, 3 or anything else. I have tried a bunch of other ways but none seems to work.
Note: response.succes(or error) is not being called anywhere, the code is definitely waiting for object.save() to be completed.
var promise = new Parse.Promise();
var query = new Parse.Query("SomeClass");
query.find().then(function(results) {
var promises = [];
results.forEach(function(object) {
object.set("SomeColumnName", true);
console.log('1');
promises.push(object.save(null, {
success: function(result) {
alert('2');
return ;
},
error: function(result, error) {
alert('3');
return ;
}
}));
});
Parse.Promise.when(promises).then(function() {
console.log('inside resolve');
promise.resolve();
}, function() {
console.log('inside reject');
promise.reject();
});
});
return promise;
You're on the right track, but you should take advantage of the fact that most of the sdk functions create and return promises for you. With those, you can substantially simplify the code:
// very handy utility library that provides _.each among many other things
// www.underscorejs.org
var _ = require('underscore');
// answer a promise to modify all instances of SomeClass
function changeSomeClass() {
var query = new Parse.Query("SomeClass");
// if there are more than 100 rows, set query.limit up to 1k
return query.find().then(function(results) { // find returns a promise
_.each(results, function(result) {
result.set("SomeColumnName", true);
});
return Parse.Object.saveAll(results); // and saveAll returns a promise
});
}
Wrap it in a cloud function and call success/error like this:
Parse.Cloud.define("changeSomeClass", function(request, response) {
changeSomeClass().then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
});
You can only have one Parse request happening at a time for each object. If multiple requests are sent, all but the first are ignored. You're probably trying to do this while other threads are making Parse requests for those objects. I know that if you save an object, it also saves it's child objects, so you could be hitting a problem with that. Make sure you do as much as you can in background threads with completion blocks, or use saveEventually / fetchEventually where possible.
I have a function which issues two async requests before yielding some data.
The caller of the method does not need to know about its implementation details. All the caller needs is:
The data returned from the second request.
The ability to call abort and not be returned data.
This is complicated by the fact that abort can be called after the first promise is done. The second request is already in-flight, but the caller has yet to receive data. So, the caller assumes it can call abort, but rejecting the first promise will have no effect.
I work around this issue with the following, but it feels pretty hacky. Am I missing something?
var ajaxOptions = {
url: 'https://www.googleapis.com/youtube/v3/search',
data: {
part: 'id',
key: 'AIzaSyDBCJuq0aey3bL3K6C0l4mKzT_y8zy9Msw',
q: 'Hello'
}
};
function search(options) {
var jqXHR = $.ajax(ajaxOptions);
var innerJqXHR = null;
jqXHR.then(function(data, statusText, jqXHR) {
innerJqXHR = $.ajax(ajaxOptions);
innerJqXHR.done(options.done);
innerJqXHR.fail(options.fail);
return innerJqXHR;
}, options.fail);
return {
promise: jqXHR,
innerPromise: innerJqXHR,
fullAbort: function() {
jqXHR.abort();
if (innerJqXHR !== null) {
innerJqXHR.abort();
}
}
}
}
var searchReturn = search({
done: function() {
console.log('inner done');
},
fail: function() {
console.log('inner or outer fail');
}
});
searchReturn.fullAbort();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Since your code looks a little like pseudo-code (unanswered questions in it for me), the best I can offer is a framework of an idea. The general idea is that you return two things from your search function, a promise that is resolved only when both ajax calls are resolved and an abort function that can be called to abort the process.
function search(options) {
var activeAjax = $.ajax(args for first ajax call).then(function(data) {
// second ajax call changes activeAjax so abort will work
// on the active ajax call
activeAjax = $.ajax(args for second ajax call).then(function(data) {
activeAjax = null;
// collect your final data here and return it
// this will be the resolved value of the final promise
return finalData;
});
return activeAjax;
});
return {
promise: activeAjax,
abort: function() {
if (activeAjax) {
activeAjax.abort();
}
}
};
}
// usage:
var searchInProgress = search(...);
searchInProgress.promise.then(function(data) {
// search finished successfully, answer is in data
}, function(err) {
// either one of the promises failed
});
// and, at any time, you can call searchInProgress.abort();
I have the following data
var data = [{user:"somename"}, {user:"anothername"}];
I have this function that processes that that, let say checking user if it exist
function process(user) {
$.ajax({
url: 'http://domain.com/api',
success: function(result) {
if(result == 'success')
anotherFunction();
else
console.log('error');
}
})
}
function anotherFunction() {
$.ajax({
// do stuffs
})
}
$.each(data, function(k,v){
process(v.user);
})
The $.each will try to loop even the process and the anotherFunction hasn't finished yet
Question, what can I do to assure that all functions are finished executing before moving on to another index?
I heard I can use jquery deferred.
Collect the promises returned by your AJAX function, if necessary post-processing that result with .then so that it calls anotherFunction() which must also return the result of $.ajax.
function process() {
return $.ajax(...).then(function(result) {
if (result === 'success') {
return anotherFunction();
} else {
return null; // this might need to change...
}
});
}
function anotherFunction() {
return $.ajax(...);
}
var promises = [];
$.each(data, function(k, v) {
promises.push(process(v.data));
}
and then wait for all the promises to be resolved:
$.when.apply($, promises).done(function() {
// all done here
});
NB: in general it's good practise for an AJAX call to produce a non 2xx return code for errors, rather than an error code within a "successful" HTTP call. This then allows the client side code to use normal AJAX error processing (i.e. .fail callbacks) instead of checking for "errors" within AJAX success processing.