Jquery Deferred. Multiple ajax calls. deferred vs async false - javascript

I've got the following.
var lookupInit = function () {
http.get('api/employmenttype', null, false)
.done(function (response) {
console.log('loaded: employmenttype');
vm.lookups.allEmploymentTypes(response);
});
http.get('api/actionlist', null, false)
.done(function (response) {
console.log('loaded: actionlist');
vm.lookups.allActionListOptions(response);
});
http.get('api/company', null, false)
.done(function (response) {
console.log('loaded: company');
vm.lookups.allCompanies(response);
});
//... x 5 more
return true;
};
// somewhere else
if (lookupInit(id)) {
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
http.json('api/adimport', { by: "latest" }, false).done(viewInit);
}
else if (id !== undefined) {
console.log('api/adimport: transaction');
http.json('api/adimport', { by: "transaction", TransactionId: id }, false).done(viewInit);
}
} else {
console.log('User info init failed!');
}
The following "http.get('api/employmenttype', null, false)" means i set async to false.
I'm aware that this is probably inefficient. And i'd like to have all the calls load simultaneously.
The only problem is if i don't have them set to async false, the second part of my code might execute before the dropdowns are populated.
I've tried a couple of attempts with Jquery Deferreds, but they have resulted in what i can only describe as an abortion.
The only thing i'm looking to achieve is that the lookup calls finish before the adimport/second part of my code, in any order.... But having each call wait for the one before it to finish EG: async, seems like the only solution I'm capable of implementing decently ATM.
Would this be an appropriate place for deferred function, and could anyone point me into a direction where i could figure out how to implement it correctly, as I've never done this before?

You can use $.when to combine multiple promises to one that resolves when all of them have been fulfilled. If I got you correctly, you want
function lookupInit() {
return $.when(
http.get('api/employmenttype').done(function (response) {
console.log('loaded: employmenttype');
vm.lookups.allEmploymentTypes(response);
}),
http.get('api/actionlist').done(function (response) {
console.log('loaded: actionlist');
vm.lookups.allActionListOptions(response);
}),
http.get('api/company').done(function (response) {
console.log('loaded: company');
vm.lookups.allCompanies(response);
}),
// … some more
);
}
Then somewhere else
lookupInit(id).then(function(/* all responses if you needed them */) {
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
return http.json('api/adimport', {by:"latest"})
} else {
console.log('api/adimport: transaction');
return http.json('api/adimport', {by:"transaction", TransactionId:id});
}
}, function(err) {
console.log('User info init failed!');
}).done(viewInit);

In the Jquery API I've found this about resolving multiple deferreds:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively.
each argument is an array with the following
structure: [ data, statusText, jqXHR ] */
var data = a1[0] + a2[0]; /* a1[0] = "Whip", a2[0] = " It" */
if ( /Whip It/.test(data) ) {
alert("We got what we came for!");
}
});
Using this with your code:
var defer = $.when(
$.get('api/employmenttype'),
$.get('api/actionlist'),
$.get('api/company'),
// ... 5 more
);
defer.done(function (arg1, arg2, arg3 /*, ... 5 more*/) {
vm.lookups.allEmploymentTypes(arg1[0]);
vm.lookups.allEmploymentTypes(arg2[0]);
vm.lookups.allEmploymentTypes(arg3[0]);
// .. 5 more
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
http.json('api/adimport', { by: "latest" }, false).done(viewInit);
} else if (id !== undefined) {
console.log('api/adimport: transaction');
http.json('api/adimport', { by: "transaction", TransactionId: id }, false).done(viewInit);
}
});
You can use the defer of the $.when() inside an other $.when(), so if the json calls are not dependant on the first calls you can add them in a an onther defer.

Related

How to make sequential if statements asynchronous in JavaScript?

I have a series of if statements in a loop like this:
for( var i = 0; i < results_list.length; i++){
find = await results_list[i];
//result 1
if (find.Process == "one") {
await stored_proc(38, find.Num, find.Status)
}
//result 2
if(find.Process == "two") {
await stored_proc(37, find.Num, find.Status)
}
//result 3
if(find.Process == "three") {
await stored_proc(39, find.Num, find.Status)
}
}
My issue is that it runs all of these synchronously causing my stored procedure to trip over itself. How can I ensure each if statement waits for the previous one to complete before running?
It is also important to know that each if statement is not always run, for instance on one run of my code //result 2 may run and //result 1 and //result 3 may not run.
Sometimes they might all run and sometimes none will run at all.
Thanks for any help!
EDIT: Here is my stored procedure function
async function stored_proc(opID, num, stat){
sql.executeTransaction( connection, {
procedure: "<stored procedure>",
params: {
OpID: {
val: opID,
type: sql.INT
},
num: {
val: num,
type: sql.STRING
},
Pass: {
val: stat,
type: sql.INT
},
ExtraData: {
val: "upload",
type: sql.STRING
}
}
} ).then( async function( data ) {
return data.transaction
.commit()
.then( async function() {
console.log("Updated database...." );
} );
}, function( err ) {
console.log( err );
} );
}
SECOND EDIT: I have looked into this some more and found that the if there is more than one result to upload it will NEVER upload the first sets of results. I have ran some console.log()s through and found it will always get find.Num and find.Status. It will only log Updated database for every result after the first one. I hope this makes sense
In your stored_proc you are not returning the Promise.
Also, promise inside promise is considered anti-pattern you can chain it easily.
async function stored_proc(opID, num, stat) {
return sql.executeTransaction(connection, {
procedure: "<stored procedure>",
params: {
OpID: {
val: opID,
type: sql.INT
},
num: {
val: num,
type: sql.STRING
},
Pass: {
val: stat,
type: sql.INT
},
ExtraData: {
val: "upload",
type: sql.STRING
}
}
})
.then(function (data) {
return data.transaction
.commit()
})
.then(function () {
console.log("Updated database....");
})
.catch((err) => {
console.log(err);
})
}

Node.js and mysql callbacks

I am new to node.js and callbacks. I have to search in 4 different tables depending on user's search configurations. There could be max 4 search queries. For example:
Table-1
city
Table-2
country
Table-3
diet
Table-4
product
If user's configuration has city and country name then I need to search in Table-1 and Table-2. If I find any relevant information in any table, I need to return true and stop checking rest of the tables. If I cannot find relevant information in any table, I need to return false.
Mysql connector: "mysql": "^2.13.0"
Pseudo code:
function search(searchTerm, type, cb) {
if (type=="city") {
searchDB(searchTerm, "city", "fct", function (result) {
if (result == true) cb(result)
});
}
if (type=="country") {
searchDB(searchTerm, "country", "fcy", function (result) {
if (result == true) cb(result)
});
}
if (type=="diet") {
searchDB(searchTerm, "diet", "fdt", function (result) {
if (result == true) cb(result)
});
}
if (type=="product") {
searchDB(searchTerm, "product", "fpt", function (result) {
if (result == true) cb(result)
});
}
}
function searchDB(searchTerm, tableName, fieldName, cb) {
var qryParams;
var qry = 'SELECT * from ?? where ??=?';
qryParams = [tableName, fieldName, searchTerm];
qry = mysql.format(qry, qryParams);
connection.query(qry, function (error, results, fields) {
if (error) throw error;
if (results == "") {
return cb(false);
} else {
return cb(true);
}
});
}
In your situation you should use Promise.race() function. Not posting entire example here because a great and complete example is shown on MDN page.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
Promise.race([p3, p4]).then(function(value) {
console.log(value); // "three"
// p3 is faster, so it resolves
}, function(reason) {
// Not called
});
EDIT. Just realized you might not be using promises... Have a read about them, Promises are intended to fight the dreaded callback hell.

Using angular using $interval or a promise to run code when api is completed?

I have an api call to that is a bit slow.
$http.jsonp(url, {
params: {
'client': 'translate_about',
'alpha': 1,
'hl': 'en'
}
})
.success(function (data) {
home.translate.list = data.sl;
//console.log(data.sl);
});
this next block can happen before or in parallel with home.translate.list is ready. Therefore, when home.translate.list is successful I need to another block of code to update.
home.modalOn = function (val) {
//open modal
home.isModal = true;
//this if block must wait for home.translate.list to be ready.
if(typeof home.translate.list !== 'undefined'){
home.activePageSelection = home.translate.list[val];
//call wikipedia and get data
home.callWiki(home.translate.list[val]);
}
};
$scope.$watch(function () {
return location.hash
}, function (value) {
var currentRouteParam = value.split('/').slice(-1)[0];
if (currentRouteParam !== '') {
home.modalOn(currentRouteParam);
} else {
home.modalOff()
}
});
Question:
How can I make sure home.translate.list is defined before executing the if block? Caveat, without putting it in the $http.success block.
Try like this
if (currentRouteParam !== '') {
var interval = $interval(function() {
if (typeof home.translate.list !== 'undefined') {
$interval.cancel(interval);
home.modalOn(currentRouteParam);
}
}, 500);
}
N:B:
you have to inject $interval service in your controller
I suggest you to put the watch in a function and call it in the success block
.success(function (data) {
home.translate.list = data.sl;
watchHash();
//console.log(data.sl);
});
function watchHash() {
$scope.$watch(function () {
...
}
You could save the promise for the translation instead of the list in your vm:
home.translatePromise = $http.jsonp(url, {
params: {
'client': 'translate_about',
'alpha': 1,
'hl': 'en'
}
})
.then(function (response) {
return response.data.sl;
});
Then reference the promise instead of the raw value:
home.modalOn = function (val) {
//open modal
home.isModal = true;
//this if block must wait for home.translate.list to be ready.
home.translatePromise.then(function(list){
home.activePageSelection = list[val];
//call wikipedia and get data
home.callWiki(list[val]);
});
};

Jquery Ajax prevent fail in a deferred sequential loop

So, I'm chaining together sequential ajax, to load an array of urls in order. Originally I used .then() instead of .always(), and either way it works fine - provided that all urls existed. However, since there's a possibility of missing files, I wanted to compensate for that, and then finally, inform the user of which files were missing, in order to make it easier to rectify.
However, the problem is, on a missing file/404, the code executes, like it should, but then exits the loop, preventing any further ajax calls. So I figure, I need some way of either handling the fail() and forcing a success regardless, or some other way of overriding the default behavior on a 404, so it continues progressing through the loop.
Unfortunately, the closest Google results, were how to do the opposite (force a failure on success).
var missing=[];
uLoadList.reduce(function(prev, cur, index) {
return prev.then(function(data) {
return $.ajax("/wiki/"+cur).always(function(data) {
var temp = $('#mw-content-text',data);
temp = $('pre',temp);
if(temp.length > 0)
{
//handle success
}else{
//handle failure
missing.push(cur);
}
});
});
}, $().promise()).done(function() {
if(missing.length > 0)
{
//notify of missing objects
}
//continue on.
});
One final note, to alleviate confusion: the URLs, and the script itself, are on a MediaWiki site - so even if a 404 is returned, there will always be page content, and will contain the element with the id of "mw-content-text".
Try
(function ($) {
$.when.all = whenAll;
function whenAll(arr) {
"use strict";
var deferred = new $.Deferred(),
args = !! arr
? $.isArray(arr)
? arr
: Array.prototype.slice.call(arguments)
.map(function (p) {
return p.hasOwnProperty("promise")
? p
: new $.Deferred()
.resolve(p, null, deferred.promise())
})
: [deferred.resolve(deferred.promise())],
promises = {
"success": [],
"error": []
}, doneCallback = function (res) {
promises[this.state() === "resolved"
|| res.textStatus === "success"
? "success"
: "error"].push(res);
return (promises.success.length
+ promises.error.length) === args.length
? deferred.resolve(promises)
: res
}, failCallback = function (res) {
// do `error` notification , processing stuff
// console.log(res.textStatus);
promises[this.state() === "rejected"
|| res.textStatus === "error"
? "error"
: "success"].push(res);
return (promises.success.length
+ promises.error.length) === args.length
? deferred.resolve(promises)
: res
};
$.map(args, function (promise, index) {
return $.when(promise).always(function (data, textStatus, jqxhr) {
return (textStatus === "success")
? doneCallback.call(jqxhr, {
data: data,
textStatus: textStatus
? textStatus
: jqxhr.state() === "resolved"
? "success"
: "error",
jqxhr: jqxhr
})
: failCallback.call(data, {
data: data,
textStatus: textStatus,
jqxhr: jqxhr
})
})
});
return deferred.promise()
};
}(jQuery));
// returns `Object {
// success: Array[/* success responses*/],
// error: Array[/* error responses */]
// }`
// e.g.,
var request = function (url, data) {
return $.post(url, {
json: JSON.stringify(data)
})
}, settings = [
["/echo/json/", "success1"], // `success`
["/echo/jsons/", "error1"], // `error`
["/echo/json/", "success2"], // `success`
["/echo/jsons/", "error2"], // `error`
["/echo/json/", "success3"] // `success`
];
$.when.all(
$.map(settings, function (p) {
return request.apply($, p)
})
)
.then(function (data) {
console.log(data);
// filter , process responses
$.each(data, function(key, value) {
if (key === "success") {
results.append(
"\r\n" + key + ":\r\n" + JSON.stringify(value, null, 4)
)
} else {
results.append(
"\r\n" + key + ":\r\n"
+ JSON.stringify(
value.map(function(v, k) {
v.data.responseText = $(v.data.responseText)
.filter("title, #summary, #explanation")
.text().replace(/\s+/g, " ");
return v
})
, null, 4)
)
}
})
}, function (e) {
console.log("error", e)
});
jsfiddle http://jsfiddle.net/guest271314/620p8q8h/

how to return true or false when i wont know until the callback is performed

I have an ajax function inside of a function in a backbone model. I want the function fetchByFBID to return true or false based on what the data returned consists of. But since I dont know what the data is until the callback is performed, I do not know how to return true or false in that function. Any tips?
var UserModel = Backbone.Model.extend({
urlRoot: '/API/users',
defaults: {
"fbID": "",
"firstName": "",
"lastName": "",
"gender": "",
"email": "",
"site": ""
},
fetchByFBID: function(fbID) {
var that = this;
console.log(this);
$.ajax({url: "/API/users/fbID/"+fbID, type: 'GET', success: function(data) {
if(data === "false") {
return false;
} else {
that.set(JSON.parse(data));
return true;
}
}});
}
});
To call the function I simply do:
var fbUser = new UserModel();
fbUser.fetchByFBID(response.id);
My goal is to be able to do something like:
var fbUser = new UserModel();
if(fbUser.fetchByFBID(response.id))
//do something
else
//do something else
You can either :
add a callback as an argument to your function, and asynchronously call it with true or false
var fbUser = new UserModel();
fbUser.fetchByFBID(response.id, function (result) {
if (result) {
// do something
} else {
// do something else
}
);
This should be as easy as
fetchByFBID: function(fbID, callback) {
.... do your work ....
if(data === "false") {
callback.call(null,true); // or callback(true), callback.call(whatever, true), etc...
} else {
that.set(JSON.parse(data));
callback.call(null,false);
}
instead of true or false, make your function return a "promise", so that you can write something along the lines of
var fbUser = new UserModel();
fbUser.fetchByFBID(response.id).whenDone(function (result) {
if (result) {
// do something
} else {
// do something else
}
});
I would add additional hidden or any div. In callback function set its content to 'true' or 'false' or any other values you like and use events to notify the operations that you get the ajax answer already.
This solution use kind of observer pattern.

Categories