angularjs - Sequentially call function that performs ngResource http post request - javascript

This is the outline of a function to perform and http post request to get all the entries from a table. This function is defined within my controller.
$scope.getAllData = function (tableName) {
var allDataResults = $resource('/getAllDataForTable', {}, {
save: {
method: 'POST',
timeout: 6000
}
});
allDataResults.save($scope.all_data_input, function (response) {
//Do stuff with response
}
});
};
I need to call this function sequentially for different tablenames. I tried simply calling it twice like this.
$scope.getAllData(tableName1);
$scope.getAllData(tableName2);
The second response comes in correct, but the first one is incorrect. Each response contains a list and the size of the second response's list is forced on the first one, causing the response to be incorrect. How do I properly chain these 2 post requests requests?

You need to return the promise from your function, i. e.:
$scope.getAllData = function (tableName) {
var allDataResults = $resource('/getAllDataForTable', {}, {
save: {
method: 'POST',
timeout: 6000
}
});
return allDataResults.save($scope.all_data_input, function (response) {
//Do stuff with response
}
});
};
Then, you can chain your calls using the returned promise:
$scope.getAllData(tableName1).$promise.then(function() {
$scope.getAllData(tableName2);
});
Btw the $resource examples might help you understand it better. If you need to manage a lot of chained promises, you should look at $q.all.

Related

One function to wait for another function in Javascript (AJAX)

I'm trying to find a way to make one function to hold and only return its output (hold the flow) only when the async function inside it (AJAX call) have completed.
In this click event, the "prepareEstimates" function calls another function, which makes an AJAX call to an API. The ideia is that the code won't move forward until this function has completed everything there, including the AJAX call. Because it's calling the same API, I cannot have two simultaneous call using the same data (issues with the API that are out of scope from this question).
$('#estimateBtn').on("click", function () {
if (validateBasicEstimationForm()) {
prepareEstimates();
if (validateCustomEstimationForm()) {
prepareCustomEstimates();
}
});
function prepareEstimates() {
// check if runAPI should be true or false
if (runApi) {
// make call here
$.when(callAJAXFunction(path, dataForAPICalls)).done(function (result) {
// Do stuff with the result
});
}
};
Then, once this prepareEstimates function has completed, it should move forward and the next one, prepareCustomEstimates, which is very similar to this function, will do the same thing.
function prepareEstimates() {
// check if runAPI should be true or false
if (runApi) {
// make call here
$.when(callAJAXFunction(path, dataForAPICalls)).done(function (result) {
// Do other stuff with the result
});
}
};
and this callAJAXFunction just return an AJAX call
// Invoke API call and get result
return await $.ajax({
url: url,
type: "POST",
dataType: "json",
data: data,
success: function (result) {
},
error: function () {
console.log("error calling Api");
}
});
}
I tried using promise, await, and others, but I couldn't make it work, no matter what I tried.
In the end, what is happening is that the first AJAX call is fired, then right after it, the second ajax call, before the first one has completed.
Any suggestion?
Thank you

How to use promises, or complete an ajax request before the function finishes?

I have the following function to check a users session to see if they're staff or not. Now, I know there are better ways to do this, but I'm trying to make a simple application that's tied with a forum software.
function isStaff(callback) {
$.ajax({
url: url
}).done(function(data) {
var session = $.parseJSON(data);
if (session.is_staff === 1) {
callback(true);
} else {
callback(false);
}
});
}
Let's say I'm using this function in, like so, when compiling a "post" (Handlebars).
function compilePost(post) {
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: function() {
isStaff(function(response) {
return response;
});
}
}
var html= template(context);
return html;
}
Problem here, is that the request to check if a user is staff doesn't complete the request until after the function is ran.
I know with Promises is an alternative to async: false, where request is made and the response comes back before the function finishes.
But I have no idea how I can convert this into a promise. I've tried to learn it but I'm stuck at the concept. Can someone explain this to me? Thanks.
First, let's simplify the compilePost function. This function should know how to compile a post in a synchronous manner. Let's change the isStaff fetching to a simple argument.
function compilePost(post, isStaff) {
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: isStaff
}
var html= template(context);
return html;
}
Now, let's create a new method, with a single purpose - checking if a user is member of the staff:
function checkForStaffMemebership() {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
success: function (data) {
var session = $.parseJSON(data);
if (session.is_staff === 1) {
resolve(true);
} else {
resolve(false);
}
}
});
});
}
This function wraps your original ajax call to the server with a promise, whenever the $.ajax call gets a response from the server, the promise will resolve with the answer whether the user is a staff member or not.
Now, we can write another function to orchestrate the process:
function compilePostAsync(post) {
return checkForStaffMemebership()
.then(function (isStaff) {
return compilePost(post, isStaff);
});
}
compilePostAsync finds out whether the user is a staff member or not. Then, it's compiling the post.
Please notice that compilePostAsync returns a promise, and thus if you used to have something like:
element.innerHTML = compilePost(post);
Now, you should change it to something like:
compilePostAsync(post).then(function (compiledPost) {
element.innerHTML = compiledPost;
});
Some notes:
This is only an example, it surely misses some things (proper error handling for example)
The isStaff and checkForStaffMemebership (original and new) do not get any argument, I guess you'd figure out how to pass the userId or any other data you might need
Read about promises, it's a useful tool to have, there is a lot of data about it on the web, for example: MDN.
As per the documentation you dont need to wrap the ajax with a promise which already implements promise. Instead chain the response as explained below.
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise (see Deferred object for more information)
You can do something like below by chaining the response:
function isStaff(url, post) {
return $.ajax({
url: url,
dataType:"json"
}).then(function(resp){
//resp = $.parseJSON(resp); /*You dont require this if you have respose as JSON object. Just Specify it in 'dataType'*/
var source = $('#feed-item-template').html();
var template = Handlebars.compile(source);
var context = {
id: post.id,
content: post.text,
author: post.author,
date: $.timeago(post.date),
staff: resp.is_staff === 1 ? true : false
};
return template(context);
});
}
isStaff(url, post).done(function(template){
/*Your compiled template code is available here*/
}).fail(function(jqXHR, textStatus, errorThrown){
console.log("Error:"+textStatus);
});
Note: Be sure to implement error callbacks also. Because you may never know what
went wrong :)
Simple explanation about promise with $.defer:
For understanding i have created the Fiddle similar to your requirement.
Explanation:
Basically Promise is been introduced to attain synchronous execution of asynchronous JS code.
What do you mean by Async or Asynchronous code?
The code that is executed may return a value at any given point of time which is not immediate. Famous example to support this statement would be jquery ajax.
Why is it required?
Promise implementations helps a developer to implement a synchronous code block which depends on asynchronous code block for response,. like in ajax call when i make a request to server asking for a data string, i need to wait till the server responds back to me with a response data string which my synchronous code uses it to manipulate it , do some logic and update the UI.
Follow this link where the author has explained with detailed examples.
PS: Jquery $.defer implements or wraps promise in quite a different way. Both are used for the same purpose.
let basedataset = {}
let ajaxbase = {};
//setting api Urls
apiinterface();
function apiinterface() {
ajaxbase.createuser = '/api/createuser'
}
//setting up payload for post method
basedataset.email = profile.getEmail()
basedataset.username = profile.getGivenName()
//setting up url for api
ajaxbase.url = ajaxbase.createuser
ajaxbase.payload = basedataset;
//reusable promise based approach
basepostmethod(ajaxbase).then(function(data) {
console.log('common data', data);
}).catch(function(reason) {
console.log('reason for rejection', reason)
});
//modular ajax (Post/GET) snippets
function basepostmethod(ajaxbase) {
return new Promise(function(resolve, reject) {
$.ajax({
url: ajaxbase.url,
method: 'post',
dataType: 'json',
data: ajaxbase.payload,
success: function(data) {
resolve(data);
},
error: function(xhr) {
reject(xhr)
}
});
});
}
A solution using async await in js would be like this:
async function getMyAjaxCall() {
const someVariableName = await ajaxCallFunction();
}
function getMyAjaxCall() {
return $.ajax({
type: 'POST',
url: `someURL`,
headers: {
'Accept':'application/json',
},
success: function(response) {
// in case you need something else done.
}
});
}

Promise always returns the initial value

having a bit of a problem with promises in angularjs.
My promises 'get cached', meaning they always return the initial value they got called with. I'm pretty familiar with promises from other places, but new to angularJS, so please help me shed a light on my problem, I'm probably not understanding something very basic here
I am using a factory:
.factory('Traffic', function ($http) {
var trafficUrl = 'some url';
var httpPromise = $http.get(trafficUrl, {cache: false} );
var invalidateCache = function() {
return $http.get(trafficUrl, {cache: false} );
}
return {
all: function () {
httpPromise = invalidateCache();
return httpPromise
.then(function (response) {
//parsing the response and returning stuff (not promise)
}
}
})
which is sending a request, and parsing it for the first time.
now invalidateCache was suggested by someone to avoid exactly my problem (assign a new $http.get each time to avoid it referring to the same, initial promise).
now my controller:
.controller('TrafficCtrl', function ($interval, $ionicLoading, $scope, Traffic) {
var getJams = function () {
var traffic = Traffic.all();
traffic.then(function (response) {
//doing stuff with the response
})
};
$scope.refresh = function () {
getJams();
}
})
now, each time I invoke $scope.refresh method, I see the items getting 'refreshed' (console.log gets called inside getJams) but the values stay as the first called getJams().
Thanks.
From your comment, it sounds like the browser is caching your response, so you will want to update the server logic to set Cache specific headers.
You will probably want to add the following Cache-Control header to your response:
Cache-Control: no-store, no-cache
A little more info about the cache headers here.
Should be able to find plenty of examples to set this in your server side language of choice.
Also, you can clean up the code much more:
.factory('Traffic', function ($http) {
var trafficUrl = 'some url';
return {
all: function () {
// don't need the invalidate call anymore
return $http.get(trafficUrl).then(function (response) {
//parsing the response and returning stuff (not promise)
}
}
})
And your controller:
var getJams = function () {
// No need to store the initial promise call, just chain them.
Traffic.all().then(function (response) {
//doing stuff with the response
})
};

Javascript: is there a better way to execute a function after x amount of async database/ajax calls

using Backbone.js we have an application, in which on a certain occasion we need to send an ajax post to a clients webservice.
however, the content to be posted, is dynamic, and is decided by a certain array.
for each item in the array we need to go fetch a piece of data.
after assembling the data that aggregated object needs to be sent.
as of now, i have a synchronous approach, though i feel that this is not the best way.
var arrParams = [{id: 1, processed: false},{id: 7, processed: false},{id: 4, processed: false}];
function callback(data) {
$.post()... // jquery ajax to post the data... }
function fetchData(arr, data, callback) {
var currentId = _(arr).find(function(p){ return p.processed === false; }).id; // getting the ID of the first param that has processed on false...
// ajax call fetching the results for that parameter.
$.ajax({
url: 'http://mysuperwebservice.com',
type: 'GET',
dataType: 'json',
data: {id: currentId},
success: function(serviceData) {
data[currentId] = serviceData; // insert it into the data
_(arr).find(function(p){ return p.id === currentId; }).processed = true; // set this param in the array to 'being processed'.
// if more params not processed, call this function again, else continue to callback
if(_(arr).any(function(p){ return p.processed === false }))
{
fetchData(arr, data, callback);
}
else
{
callback(data);
}
},
error: function(){ /* not important fr now, ... */ }
});
}
fetchData(arrParams, {}, callback);
isn't there a way to launch these calls asynchronous and execute the callback only when all results are in?
You have to use JQuery $.Deferred object to sync them. Look at this article Deferred Docs
You can use in this way:
$.when(
$.ajax({ url : 'url1' }),
$.ajax({ url : 'url2' }) // or even more calls
).done(done_callback).fail(fail_callback);
I would do something like this:
make a function that besides the parameters that you pass to fetchData also gets the index within arrParams, then in a loop call that function for every element. In the success function set processed in your element to true, and check if "you're the last" by going through the array and see if all the rest is true as well.
A bit of optimization can be if you make a counter:
var todo = arrParams.length;
and in the success you do:
if (--todo == 0) {
callback(...)
}

Chaining multiple jQuery ajax requests

I have the following code:
$.when(loadProjects())
.then(function() {
$.when.apply($, buildRequests(projects))
.then(function(data) {
$.when.apply($, vcsRequests(buildTypes))
.then(function(data) {
$.when.apply($, vcsDetailRequests(vcsRoots))
.then(function(data) {
alert('done');
});
});
});
});
Each of the functions passed into when.apply() return arrays of requests. I cannot perform the buildRequests calls until the calls from loadProjects() has finished as they rely on information returned from those calls. Each call depends on information returned by the previous call, so they must be in this order. I need to know when all the calls have finished so I can process the data returned.
Is there a cleaner way to approach this?
I came across yepnope.js the other day. I haven't tried it myself yet, but it might be helpful if you're doing a lot of ajax loading.
Actually thinking this over makes me realize that yepnope.js is not really applicable to your case. What I would consider in your case is to have loadProjects() et al return a single promise by applying when() internally in each function. Also putting pipe() to use could lead to something like
loadProjects().pipe(buildRequests).pipe(vcsRequests).pipe(vcsDetailRequests);
Sample buildRequests():
function buildRequests(projects){
// Do something using projects
// ...
var requestsPromise = ...; // Finally get ajax promise for requests
return requestPromise;
}
The result of the requestPromise will then be passed into the next piped function once it is resolved/rejected.
From the docs on pipe():
// Example: Chain tasks:
var request = $.ajax( url, { dataType: "json" } ),
chained = request.pipe(function( data ) {
return $.ajax( url2, { data: { user: data.userId } } );
});
chained.done(function( data ) {
// data retrieved from url2 as provided by the first request
});
.... response according to my comment on original post:
Seems you have lot of requests to chain. I would then consider
combining all request into single one.... much more efficient than
chaining...
Well, something like this:
PHP:
$projects = YourAPI::loadProjects();
$builds = YourAPI::getBuilds($projects);
$vcs = YourAPI::getVCS($builds);
$details = YourAPI::getVCSDetails($vcs);
// for example
return json_encode($details);
// OR, if you need all the data
$results = array(
"projects" => $projects,
"builds" => $builds,
"vsc" => $vcs,
"details" => $details
);
return json_encode($results);
This way, you have inherent synhronization between calls AND less HTTP trafiic ;)
Dependence chain of AJAX requests : You can chain multiple AJAX request — for example, first call retrieves the user details of a user, and we need to pass that value to second script. Remember that $.then() returns a new promise, which can be subsequently passed to the $.done() or even another $.then() method.
var a1 = $.ajax({
url: '/first/request/url',
dataType: 'json'
}),
a2 = a1.then(function(data) {
// .then() returns a new promise
return $.ajax({
url: '/second/request/url',
dataType: 'json',
data: data.userId
});
});
a2.done(function(data) {
console.log(data);
});

Categories

Resources