Passing Promise as parameter doesn't trigger then or fail callbacks - javascript

Even though i am able to pass promise as parameter into error function, it does not trigger then & error function on the caller.?
if i change the code to put the AJAXError function inline, everything works fine.
Is it possible to pass around the promise around as parameter and still be able to trigger back then and fail on the caller up in the chain.
NOTE : promise is returned from palletizerService.addSerialNumberToPallet but but resolved/rejected in a different function AjaxFailed.
palletizerService.addSerialNumberToPallet ('p1','s1')
.then( function (response){
console.log('success');
},
function(error){
console.error(error);
});
Hereunder is the service that i am invoking from the above code
palletizerService.addSerialNumberToPallet = function (palletID, serialNumber) {
var deferred = $q.defer();
$.ajax({
url: url,
type: "GET",
dataType: "json",
methodName: methodName,
deferred: deferred.promise,
data: "palletID=" + palletID + "&serialNumber=" + serialNumber,
timeout: 20000, // 20 seconds for getting result, otherwise error.
error: AjaxFailed,
complete: function (jqXHR, textStatus) {
console.log("Ajax-finally : " + methodName);
},
beforeSend: function (qXHR, settings) {
},
success: function (data, textStatus, jqXHR) {
console.log("Ajax-Sucess " + methodName);
deferred.resolve(data);
}
});
return deferred.promise;
};
This is the external function
function AjaxFailed(qXHR, textStatus, errorThrown) {
try {
console.error('Ajax-Error in ' + methodName, qXHR.status, qXHR.responseText);
} catch (e) {
console.log("LocalMethod-Error in " + methodName);
console.error(e);
}
finally {
this.deferred.reject(qXHR);
}
}

Your problem is that you are passing the promise as the .deferred property of your options object. You cannot resolve/reject promises, they don't have a .reject method (and your code should throw when trying to call it). You would instead need to pass the deferred object you are holding.
However, this is a bad idea anyway. As jQuery.ajax does already return a promise, you should simply assimilate it using $q.when:
function (palletID, serialNumber) {
return $q.when($.ajax({
…
}));
}
(you will be able to omit all success and errror handlers, angular-promise does take care of them; if you want to add transformations or logging do those on the promise).
Yet that's still not optimal, in fact there hardly is a reason to use jQuery.ajax at all. Just use Angular's $http service, which also returns an angular promise right away.

Related

How can I determine all ajax requests are resolved?

Here is my code:
function ajaxRequest(value, path, website){
var status = false;
return new Promise(function (resolve, reject) {
window[website] = $.ajax({
url : path,
type : 'GET',
data: { "name": value,
"_token": $('meta[name="_token"]').attr('content')
},
beforeSend: function(){
if(window[website] != null) {
window[website].abort();
}
},
success: function (people) {
status = true;
resolve([status, people]);
},
error: function (jqXHR, textStatus, errorThrown) {
reject([status, textStatus]);
},
timeout: 20000
});
});
}
And I call the function like this:
ajaxRequest('Jack', 'search/twitter', 'twitter').then(function(res) { console.log(res)}, function(err){console.log(err)});
ajaxRequest('Jack', 'search/instagram', 'instagram').then(function(res) { console.log(res)}, function(err){console.log(err)});
Now I need to know those two ajax requests are done. How can I do that?
Noted that I think I have to use promise.all(), but not sure how can I use it in my case.
You are right, promise.all() is invented to solve exactly that problem.
All it does is to return a new Promise that will resolved when all the given Promises are resolved.
In your case, you can wrap your 2 ajax calls with Promise.all something like that:
promise.all([
ajaxRequest('Jack', 'search/twitter', 'twitter').then(function(res) { console.log(res)}, function(err){console.log(err)}),
ajaxRequest('Jack', 'search/instagram', 'instagram').then(function(res) { console.log(res)}, function(err){console.log(err)})
]).then(([response1, response2]) => {
// Here you execute your logic when both of the promises are resolved.
})
You can pass function calls to $.when(). Note, jQuery.ajax() returns a jQuery promise object, using Promise constructor is not necessary
$.when(ajaxRequest(), ajaxRequest())
.then(function(...results) {
// do stuff with `results` array
})
.fail(function(jqxhr, textStatus, errorThrown) {
console.error(errorThrown)
})

Make JSON loaded via function available to other functions

I'm trying to load JSON with a JS function, and then make the JSON objects that are loaded available to other functions in the same namespace. I've tried using return to serve up the array of objects retrieved, but that doesn't work. In the attached example, I've assigned the array of objects to a property in the namespaced object, but when I try to get that array of objects outside the main loadData function, all I get is null.
Here's my JS:
var myObj = {
jsonEndPoint: '/test/test.json',
dataObjects: null
}
myObj.loadData = function () {
$.ajax({
url: 'test.json',
type: 'POST',
dataType: 'json',
success: function (data) {
myObj.dataObjects = data.apiResults[0].league.season.draft.rounds[0].picks;
//console.log(myObj.dataObjects);
},
error: function (xhr, textStatus, errorThrown) {
console.log('Data Load Error: ' + textStatus);
}
});
}()
myObj.displayData = function() {
console.log(myObj.dataObjects)
}()
The full example can be seen here: http://zbl.me/test/index.html
The JSON file I'm loading is here: http://zbl.me/test/test.json
That is because JavaScript is asynchronous in nature — when you attempt to access the myObj.dataObjects in the myObj.displayData function, that object does not yet exist because the AJAX call has not been completed yet.
What you could do is that ensure that all functions that require newly added data from the AJAX call be run only when a .done() promise has been delivered from your AJAX call, by using $.when(). The logic is quite straightforward:
myObj.loadData() is now exclusively used to make the AJAX call. With regards to how we handle the done and fail events (which are previously .success() and .error() callbacks), we delegate that logic to the next function.
myObj.displayData() is now use to evaluate the promise returned by your AJAX call made with myObj.loadData(). You use $.when() to fetch the promise, and then simply chain .done() to deal with a successful call and .fail() to deal with the opposite :)
Here's your improved code:
var myObj = {
jsonEndPoint: '/test/test.json',
dataObjects: null
}
myObj.loadData = function () {
// We return the AJAX object so that we can evaluate the state later
// This is very simple :)
return $.ajax({
url: 'test.json',
type: 'POST',
dataType: 'json'
});
}()
myObj.displayData = function() {
// Instead of using the deprecated .success() and .error()
// ... we use .done() and .fail()
$.when(myObj.loadData).done(function(data) {
myObj.dataObjects = data.apiResults[0].league.season.draft.rounds[0].picks;
}).fail(function(xhr, textStatus, errorThrown) {
console.log('Data Load Error: ' + textStatus);
});
}()
If you are unsure, you can check the dummy code here: http://jsfiddle.net/teddyrised/5rbd2eqq/1/ I have used the built-in JSON response from JSfiddle to generate an artificial response, but the logic is exactly the same as yours.
Your displayData method is called even before the ajax is completed. So you either need to call displayData in the success callback of ajax or change the structure a bit so that its easy to call.
Why don't you instead do something like this
var myObj = {
jsonEndPoint: '/test/test.json',
dataObjects: null,
displayData: function() {
console.log(this.dataObjects);
},
loadData: function() {
$.ajax({
context: this,
url: 'test.json',
type: 'GET',
dataType: 'json',
success: function(data) {
this.dataObjects = data.apiResults[0].league.season.draft.rounds[0].picks;
console.log(myObj.dataObjects);
this.displayData();
},
error: function(xhr, textStatus, errorThrown) {
console.log('Data Load Error: ' + textStatus);
}
});
}
};
myObj.loadData();
Here is a demo

Variable Scope in Nested AJAX Calls

I have a set of custom user data that I want to make an ajax call to, and in the event that there is no user data, make another ajax call to retrieve a default set of data, and then execute a function after parsing the data. Here's an example:
var oData = [],
exampleUrl = 'example.php';
$.ajax({
url: exampleUrl + '?query=getUserData',
contentType: 'application/json;odata=verbose',
headers: {
'accept': 'application/json;odata=verbose'
},
success : function(data, request){
// Request succeeded
// Check the results
if(data.length){
// There are custom user results!
// Parse the results
oData = data;
}
else{
// There were no custom user results...
// Run another query to retrieve default values
$.ajax({
url: examplUrl + '?query=getDefaultData',
contentType: 'application/json;odata=verbose',
headers: {
'accept': 'application/json;odata=verbose'
},
success : function(data, request){
// Request succeeded
// Check the results
if(data.length){
// There was some default data!
// Parse the results
oData = data;
}
else{
// No data was found...
// Attempt to be helpful
console.log('No Default data was found!');
}
},
error : function(data, request){
// There was an error with the request
// Attempt to be helpful
console.log('Error retrieving data:');
console.log(data);
console.log(request);
}
});
}
},
error : function(data, request){
// There was an error with the request
// Attempt to be helpful
console.log('Error retrieving Custom User data:');
console.log(data);
console.log(request);
},
complete : function(){
// Do something with the data
index.displayData(oData);
}
});
The issue is that if the second ajax call is run, oData doesn't contain any data at all when it's passed to index.displayData(). I'm guessing it has something to do with the asyncronous nature of ajax calls, but shouldn't 'complete' run after everything inside of 'success' runs?
I also know I probably shouldn't be using the ajax "Pyramid of Doom" and should be using promises, but I've tried them and keep getting the same results.
Thank you for your assistance!
As pointed out by Violent Crayon, you could try calling "complete" yourself instead of relying on JQuery's implicit control flow:
function getData(exampleUrl, onComplete){
$.ajax({
success : function(data, request){
if(data.length){
onConplete(data);
}else{
$.ajax({
success : function(data, request){
if(data.length){
onComplete(data);
}else{
console.log('No Default data was found!');
}
},
error : function(data, request){
console.log('Error retrieving data:');
}
});
}
},
error : function(data, request){
console.log('Error retrieving Custom User data:');
}
});
}
var oData = [];
getData('example.php', function(data){
oData = data;
index.displayData(oData);
}
BTW, note how you can have your async functions receive their own return and error callbacks. This can help reduce the pyramid of doom problem without needing to use promises and without needing to hardcode the return callback.
By working with promises, you can avoid the need to pass a callback into your function, and by defining a utility function you can avoid repetition of code.
//reusable utility function, which returns either a resolved or a rejected promise
function fetchData(queryString, cache) {
return $.ajax({
url: 'example.php',
data: { query: queryString },
type: 'JSON',//assumed
cache: cache,
contentType: 'application/json;odata=verbose',
headers: { 'accept': 'application/json;odata=verbose' }
}).then(function(data, textStatus, jqXHR) {
if (data && data.length) {
return data;
} else {
return $.Deferred().reject(jqXHR, 'no data returned').promise();//emulate a jQuery ajax failure
}
});
}
This allows promise methods to be used for a control structure, which :
is concise
uses chaining, not nesting
gives meaningful error messages.
//control structure
fetchData('getUserData', false).then(null, function(jqXHR, textStatus) {
console.log('Error retrieving Custom User data: ' + textStatus);
return fetchData('getDefaultData', true);
}).then(index.displayData, function(jqXHR, textStatus) {
console.log('Error retrieving default data: ' + textStatus);
});
Notes :
the null in .then(null, function(){...}) allows a successful response to drop straight through to the second .then(index.displayData, ...)
default data is cached while the user data is not. This is not necessary to make things work but will be faster next time the default data is required.
in the world of promises, this or something similar is the way to go.

How can I pass a callback function into an $.ajax.done callback?

I'm calling a function, which makes an ajax GET to a url, like this:
// parameters = url, callback, boolean
that.mapUrl( window.location.search, function(spec) {
console.log("initial mapping done");
console.log(spec);
// do stuff
}, true);
mapUrl will trigger an Ajax request. Inside the Ajax done or success handler, I want to trigger my callback function, but doing it like this:
$.ajax({
method: 'GET',
url: obj[1],
context: $('body')
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log("FAILED");
configuration = {
"errorThrown":errorThrown,
"textStatus": textStatus,
"jqXHR": jqXHR
}
}).done(function(value, textStatus, jqXHR) {
console.log("OK");
console.log(callback) // undefined!
configuration = {
"value":value,
"textStatus": textStatus,
"jqXHR": jqXHR
}
});
Question:
So I'm wondering how to pass my callback function into the ajax done-callback. Any idea how to do this?
Thanks!
EDIT
Here is the full mapURL function
that.mapUrl = function (spec, callback, internal) {
var key,
obj,
parsedJSON,
configuration = {"root" : window.location.href};
if (spec !== undefined && spec !== "") {
obj = spec.slice(1).split("=");
key = obj[0];
console.log(key);
switch (key) {
case "file":
$.ajax({
method: 'GET',
url: obj[1],
context: $('body')
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log("FAILED");
configuration = {
"errorThrown":errorThrown,
"textStatus": textStatus,
"jqXHR": jqXHR
}
}).done(function(value, textStatus, jqXHR) {
console.log("OK");
configuration = {
"value":value,
"textStatus": textStatus,
"jqXHR": jqXHR
}
});
break;
default:
// type not allowed, ignore
configuration.src = [];
break;
}
}
return configuration;
};
It would be generally better to preserve the "promise" interface rather than passing callbacks into your code. This will allow you to better trap error conditions.
function mapUrl(url) {
return $.ajax(...)
.fail(...)
.then(function(data) {
// preprocess data and return it
});
}
where using .then you can manipulate the returned data before it's passed to the callback:
mapUrl(...).done(function(data) {
// data has been preprocessed
...
});
If the AJAX call fails, you can chain additional .fail handlers at this point too, which your current API would not permit. This "separation of concerns" would let you put nicer error handling UI in place, for example, without cluttering your AJAX code with UI-related code.

Triggering a Javascript callback after a completed ajax call within an object

I'm trying to trigger an action after a Javascript object has been created via an AJAX call. My object looks something like this:
function API(uid,accessToken){
$.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
success: function(jsonData) {
arrayname = jsonData[values]
}
});
}
I tried to use JQuery's $.when function to do a callback after the object setup is complete (ie. the array is populated with the ajax response), which looked like this:
$.when( API = new API(uid, accessToken) ).then(function() {
...success function...
});
...but the $.when function triggers with the arrayname values still undefined. From the function's standpoint the deferred object is resolved even though the object values have not yet been set. I've since tried a number of ways to make the API object become deferred based on the completing of the entire ajax call and the setting of the variables, but I'm a bit stuck on the best way to do this.
Any pointers would be most appreciated! Thanks.
You could pass the callback function when you create the object, like so:
function API(uid,accessToken, callback){
$.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
success: function(jsonData) {
arrayname = jsonData[values]
callback(jsonData[values])
}
});
}
and then instantiate the object like so
var api = new API(uid, accessToken, function(array) {
// success function
});
If the problem is due to the "success" callback running after the "then" callbacks, you could try turning success callback into a then callback as well. I don't use JQuery but I guess it would look something like:
function API(uid,accessToken){
return $.ajax("path/to/file", {
type: "POST",
data: { user: uid, auth: accessToken },
dataType: "json",
}).then(function(jsondata){
arrayname = jsondata[values]
});
}
$.when( API = new API(uid, accessToken) ).then(function() {
// ...
});
The reason you use $.when is when you are correlating the callbacks of multiple promises, async tasks, etc. Since jQuery 1.5, all calls to $.ajax and all the wrappers ($.get and $.post) all return promises. Therefore you don't need to wrap this call with the $.when statement unless you want to do $.when(ajaxCall1, ajaxCall2).
Since you want to filter the result from the server, you should use the pipe method of promises:
function API(uid, accessToken)
return $.post(
type: 'POST'
,data: { user: uid, auth: accessToken }
,dataType: 'json'
)
.pipe(function(json) {
return json[values];
})
;
}
This allows you to write your code the way you desire:
API(uid, token)
.then(
// success state (same as promise.done)
function(arrayname /* named from your sample script*/) {
alert('success! ' + arrayname);
}
// error state (same as promise.fail)
,function(jqXHR, status, error) {
console.warn('oh noes!', error);
}
)
.done(function() { /* done #2 */ })
.fail(function() { /* fail #2 */ })
;
Note: promise.pipe() also allows you to filter (change the data passed to) the error callback as well.

Categories