2 $http get function - javascript

Given 2 JSON url, how do I make sure the code has finished retrieving the data from a.json, then only start retrieving the data from b.json, then only run init function?
var aUrl = "a.json";
var bUrl = "b.json";
My attempt:
var app = angular.module('calendarApp', []);
app.controller('ctrl', function($scope, $http) {
$http.get(aUrl).success(function(data) { });
$http.get(bUrl).success(function(data) {
init()}
);
var init = function(){}

I faced the same issue in my initial days.
There are many ways of doing it exactly as suggested here.
You need to know below two things before exploring:
1. JavaScript is synchronous
Synchronous Example[Flow in sequence]:
console.log('1')
console.log('2')
console.log('3')
It logs 1 2 3.
Example of making service calls
1. $http.get(aUrl).success(function(data) { console.log('1.any time response returns') });
2. $http.get(bUrl).success(function(data) { console.log('2.mummy returns')};
So as single-threaded javascript will first make a call to your below code with $http.get(aUrl) which hits the url and processes to fetch the data from the background.
$http.get(aUrl).success(function(data) { console.log('1.any time response returns') });
But the key thing to notice here is $http.get(aUrl) requested above doesn't wait until the data is returned in success/error. It moves to the next request $http.get(bUrl) and we just can't predict which response comes earlier.
$http.get(bUrl).success(function(data) { console.log('2.mummy returns') }
Output might be either
1.any time response returns
2.mummy returns
or
2.mummy returns
1.any time response returns
So, to overcome this situation we follow asynchronous operations in various ways.
2. Asynchronous Calls
$http.get(aUrl)
.then(function(response){
console.log('inside the first then response');
console.log(response.data);
//executing the second request after we get the first request
//and returns the **outputResponse** which is captured in the next **then** block
return $http.get(bUrl);
})
.then(function(**outputResponse** ){
console.log('outputResponse generated from to the second bUrl');
//you can call init() here
});
Above code suffices your requirement.
Click for more info using $q in future
Click here to know why to use then instead of success.

Might not be the best or cleanest method but quickly making your code do what you want it to do I got:
var app = angular.module('calendarApp', []);
app.controller('ctrl', function($scope, $http) {
$http.get(aUrl).success(function(data) {
$http.get(bUrl).success(function(data) {
init()
}
});
);
var init = function(){}

You could create a service layer in which define the two methods. Then inject the service into your controller:
//Controller
YourService.getUrl(urlA).then(function(response) {
if(response != null && response.success == true){
// do something
}
YourService.getUrl(urlB).then(function(response) {
if(response != null && response.success == true){
// do something
init()
}
},
function errorCallback(response) {
console.log("Error YourService: getUrlB ---> ");
});
},
function errorCallback(response) {
console.log("Error YourService: getUrlA ---> ");
});
// Example of method in your service
this.getUrl = function(urlA) {
try{
var deferred = $q.defer();
$http({
method: 'GET',
url: getUrlA,
params: {},
responseType: "json",
cache: false
})
.success(function(data, status, headers, config) {
deferred.resolve(data);
})
.error(function(data, status, headers, config) {
deferred.reject(data);
});
return deferred.promise;
}catch(e){
/* */
console.log("Service: getUrl ---> " + e);
}
}

$http.get returns a promise, so you can do:
return $http.get(aUrl)
.then(function(result) {
return $http.get(bUrl);
})
.then(function(result) {
return init();
},
function (error) {
// do something with the error
});

I suggest to use AngularJS promises. Mainly it has the benefit of loading the data asynchronly at the same time without having to wait until the first request is finished. see: https://docs.angularjs.org/api/ng/service/$q
var promises = [];
var loadingJson = function(url){
var defer = $q.defer();
$http.get(url).then(function(results){
defer.resolve(results);
}, function(err){
defer.reject(err);
});
return defer.promise;
};
promise.push(loadingJson('example.com/1.json'));
promise.push(loadingJson('example.com/2.json'));
$q.all(promises).then(function(resultList){
// Your hanadling here, resultList contains the results of both API calls.
}, function(errList){
// Your error handling here.
});

Related

Can I start the polling service by call ? AngularJS

i have written a polling service in AngularJS and want to start the service if my post request is done.But if I call the gui, the poll service is active.
i have try to implement a start function, end function and call the start() function if the post request is done.. but it doesnt work :/
My poll service :
.factory('NotificationPollService',
['$http', '$q', '$interval',
function ($http, $q, $interval) {
var deferred = $q.defer();
var notification = {};
notification.poller = $interval(function(id) {
$http.get('http://localhost:9999/v1/jmeter/id', {cache: false})
.success(function(data, status, headers, config) {
return data;
}, 10000);
});
notification.endPolling = function() {$interval.cancel(this.interval);};
}])
and the controller which i post the request
.controller('HomeController',
['$scope', '$rootScope', 'SendJmeterFile', 'NotificationPollService',
function ($scope, $rootScope, SendJmeterFile , NotificationPollService) {
$scope.upload = function() {
var customArtifacts = "";
var testDataBase = "";
if($scope.jmeterFile.customArtifact == undefined){
customArtifacts = null;
} else {customArtifacts = $scope.jmeterFile.customArtifact.base64}
if($scope.jmeterFile.testDataBase == undefined){
testDataBase = null;
} else {testDataBase = $scope.jmeterFile.testDataBase.base64}
SendJmeterFile.upload($scope.jmeterFile.jmeter.base64, customArtifacts, $scope.jmeterFile.customProperties, $scope.jmeterFile.instanceConfiguration, $scope.jmeterFile.instances, $scope.jmeterFile.providerID, testDataBase)
.then(function(data) {
alert("Daten erfolgreich verschickt!");
console.log(data);
NotificationPollService.poller(data.id)
//.then(function(data) {
/*if(data.status == "SETUP")
if(data.status == "TEST")
if(data.status == "DONE")
if(data.status == "ERROR")
}), function(data) {
})*/
}, function(data) {
alert("Fehler!");
console.log(data);
});
};
}])
One problem is that $interval() is called immediately upon injection into your controller. Your hunch to implement a 'Start' method or something similar was a good one - but you can probably simplify it even more by letting the factory return a function. Then you can just instantiate that function in your controller as many times as you need a Poller.
However, there are more problems. A promise can only be resolved once, and since you execute a HTTP request multiple times, my guess is that you want to be 'notified' of state changes until the state is marked as 'Done'. You're currently putting the responsibility for checking the state with the controller. If all you want is to be notified of "error" and "success" steps however, it is probably much better to let the Poller service be responsible for interpreting the state information that comes back from your service, and simply depend on standard promise behaviour in your controller. I opted to show an example of the latter case:
UPDATE: sample plnkr here: http://plnkr.co/edit/e7vqU82fqYGQuCwufPZN?p=preview
angular.module('MYMODULENAMEHERE')
.factory('NotificationPoller',
['$http', '$q', '$interval',
function ($http, $q, $interval) {
return poller;
function poller(id) {
var _this = this;
var deferred = $q.defer();
var interval = $interval(function() {
$http
// I assumed you actually want to use the value of 'id' in your
// url here, rather than just the word 'id'.
.get('http://localhost:9999/v1/jmeter/' + id, {cache: false})
.then(function(data, status, headers, config) {
// I commented out the following statement. It is meaningless because
// you can't do anything with the return value since it's an anonymous
// function you're returning it from. Instead, you probably meant to
// use the promise to return the data.
// return data;
if(data.status == "SETUP") {
deferred.notify(data);
}
else if(data.status == "TEST") {
deferred.notify(data);
}
else if(data.status == "DONE") {
_this.endPolling(); // Automatically stop polling when status is done
deferred.resolve(data);
}
else { // data.status == "ERROR" (or anything else that's not expected)
_this.endPolling(); // Automatically stop polling on error
deferred.reject(data);
}
}, function(data) {
_this.endPolling();
deferred.reject(data);
});
}, 10000);
this.endPolling = function() {
$interval.cancel(interval);
};
// Make the promise available to calling code
this.promise = deferred.promise;
};
}])
Now your controller can much more easily use your polling service. Here's an example of a stripped-down controller using your polling service, for clarity:
angular.module('MYMODULENAMEHERE')
.controller('HomeController', [
'NotificationPoller',
function(NotificationPoller) {
var some_id = 207810;
var poller = new NotificationPoller(some_id);
poller.promise.then(onSuccess, onError, onNotify);
function onSuccess(data) {
// data.status == "DONE"
};
function onError(data) {
// data.status == "ERROR"
};
function onNotify(data) {
// data.status == "TEST" || data.status == "SETUP"
};
}]);
As you see, the factory has received a little more responsibility this way, but your controller doesn't need to be aware of the details of all the statuses that the backend can send anymore. It just uses standard promises.
You try to call NotificationPollService.poller(data.id) which is Promise, actually, because previously in NotificationPollService you assigned notification.poller like so
notification.poller = $interval(function(id) {
$http.get('http://localhost:9999/v1/jmeter/id', {cache: false})
.success(function(data, status, headers, config) {
return data;
}, 10000);
});
Now your notification.poller is a return value of $interval function.
To make it work you should wrap the function so you could actually pass an id to it.

AngularJS resolve promise before load route

I'm trying to make promise inside a factory and then validate in locationChangeStart. The problem is that the locationChangeStart doesn't wait for my promise. What can I do to make my promise wait to complete?
Here is my code,
app.run(['$rootScope','$location','KeyFactory',
function($root, $location,KeyFactory) {
$root.$on('$locationChangeStart', function(event, curr, prev) {
KeyFactory.check();
console.log(KeyFactory.GetKeyPass()); ///PRINT undefined
if(KeyFactory.GetKeyPass()== true){
console.log('authorised');
}else{
$location.path('/login');
}
});
}]);
app.factory('KeyFactory', ['$http','$log', function($http,$log) {
var key = {};
key.setKeyPass = function(set) {
key.Status = set;
}
key.GetKeyPass = function() {
return key.Status;
}
key.check = function(){
$http.post('http://localhost/api/CheckPass').success(function(data) {
console.log(data);
key.setKeyPass(true);
}).error(function (data, status){
$log.error("error you cant acess here!");
console.log(status);
});
}
return key;
}]);
Asynchronous code doesn't work in synchronous way as you are thinking. After making an ajax it doesn't respond in the next line. In angular after making an ajax it return an promise object which is responsible to tell that response/error is going to happen.
There are couple of things missing in your code.
You should return a promise from the check method of service.
Then put .then function on check method promise & expect response in its success/error callback.
Code
key.check = function(){
return $http.post('http://localhost/api/CheckPass').then(function(response) {
var data = response.data;
key.setKeyPass(true);
}, function (response){
key.setKeyPass(false);
});
Run
KeyFactory.check().then(function(){
if(KeyFactory.GetKeyPass()== true){
console.log('authorised');
}else{
$location.path('/login');
}
});

How to use result of first api, into second api call in angularjs?

I want to use the result of first api, into second api call. Scenario is like that, I want to use the result of first api, into second api call. If I am correct then I want synchronous api call(not sure). I tried to write following function but not working. function2 is call before function1. In function2 we are use result1 which is only come when function1 is called before function2, How I do.
$scope.function1 = function(){
var deferred= $q.defer();
$http.post(api1, data1)
.success(function(response, status) {
if (response.error == 0) {
console.log(response.result);
$scope.result1=response.result;
}
}) ;
deferred.resolve('Success') ;
return deferred.promise;
};
var promise = $scope.addDefaultValue();
promise.then(function(){
$scope.function2();
});
$scope.function2=function(){
var deferred = $q.defer();
$http.post(api2,result1)
.success(function(response, status){
if(response.error == 0){
}
});
deferred.resolve('Success') ;
return deferred.promise;
}
You could follow promise chain pattern here, follow chaining using .then on promise object.
No need to create extra overhead promise using $q, as $http methods returns promise object when they start an ajax.
Code
$scope.function1 = function() {
return $http.post(api1, data1)
.then(function(d) {
var response = d.data;
if (response.error == 0) {
console.log(response.result);
$scope.result1 = response.result;
}
return response;
});
};
$scope.function1().then(function(data) {
$scope.function2();
}, function(error) {
});
You cannot convert $http requests to "synchronous". That's not what "deferred" does. Deferred is a way to convert non-promise-capable functions to promise-capable functions. $http functions return promise objects so you don't need to use deferred.
$http.post(api, data1).then(function (response) {
$scope.result1 = response.data.result;
// return is important here
// so that you can keep chaining with .then
return $http.post(api2, response.data.result);
}).then(function (response) {
// here you have response from api2
$scope.result2 = response.data.result;
console.log(response.data);
}).catch(function (error) {
// here you can handle errors
// from either api calls
// second api call won't be made if the first one fails
console.log(error);
});

AngularJS & Protractor - Make a http request last longer

I'm working on tests for my angularjs app and when the page is loaded some http calls are made. When any call is made a loading circle appears and when a response is received the loading circle is hidden.
How can I make the loding circle visible for let's say 10 seconds ?
You can intercept the http requests and delay them:
network-delay.js
exports.module = function() {
angular.module('networkDelayInterceptor', [])
.config(function simulateNetworkLatency($httpProvider) {
function httpDelay($timeout, $q) {
var delayInMilliseconds = 1000;
var responseOverride = function (reject) {
return function (response) {
//Uncomment the lines below to filter out all the requests not needing delay
//if (response.config.url.indexOf('some-url-to-delay') === -1) {
// return response;
//}
var deferred = $q.defer();
$timeout(
function() {
if (reject) {
deferred.reject(response);
} else {
deferred.resolve(response);
}
},
delayInMilliseconds,
false
);
return deferred.promise;
};
};
return {
response: responseOverride(false),
responseError: responseOverride(true)
};
}
$httpProvider.interceptors.push(httpDelay);
})
};
Usage
beforeAll(function() {
var networkDelay = require('network-delay');
// You can customize the module with a parameter for the url and the delay by adding them as a 3rd and 4th param, and modifying the module to use them
browser.addMockModule('networkDelayInterceptor', networkDelay.module);
});
afterAll(function() {
browser.removeMockModule('networkDelayInterceptor');
});
it('My-slowed-down-test', function() {
});
Source: http://www.bennadel.com/blog/2802-simulating-network-latency-in-angularjs-with-http-interceptors-and-timeout.htm
You can make use of setTimeout method in javascript or alternatively $timeout function in angular js.

Angular promises: get data from buffer if available

In this scenario the requirement is to get the data with an Http request if the data is not in a buffer. If it's in the buffer, use it from there without the Http request.
I tried the code below but it doesn't make much sense; if the data is in the buffer I don't know if I should return from the function doing nothing or return the deferred promise. Any thoughts?
var dataBuffer = null;
var getData = function() {
var deferred = $q.defer();
if (dataBuffer != null) { // this is the part I'm not convinced
deferred.resolve();
return;
}
$http.get('/some/url/')
.success(function(data) {
dataBuffer = data;
deferred.resolve();
})
.error(function(data) {
deferred.reject();
});
return deferred.promise;
};
Invoked in the following way:
var promise = getData();
promise.then (
function(response) {
dataBuffer = .... // dataBuffer contains data
}
);
There is a clean simple way to use promises when you're not sure which is the code you're executing is asynchronous or not and it's using $q.when
So the code can be:
var getData = function() {
return $q.when(dataBuffer ? dataBuffer: $http.get('/some/url'))
};
Then when calling getData you can use the same code you posted or just simply:
getData()
.then(function(response){//...
})
.catch(function(err){//..
});
Beware of the deferred antipattern. You can accomplish what you are trying to do very cleanly, like this:
var dataBuffer;
var getData = function() {
if (dataBuffer) {
// return a resolved promise for dataBuffer if it is already populated
return $q.when(dataBuffer);
}
$http.get('/some/url/')
.then(function (data) {
dataBuffer = data.data;
return dataBuffer;
});
};
getData().then(function (data) {
// data contains the data you want
})
.catch(function (error) {
// error occurred.
});
dataBuffer should not be accessed outside of your getData function. To make this perfectly clear, you can wrap them together in an IIFE, although this is optional:
var getData = (function () {
var dataBuffer;
return function() {
if (dataBuffer) {
// return a resolved promise for dataBuffer if it is already populated
return $q.when(dataBuffer);
}
$http.get('/some/url/')
.then(function (data) {
dataBuffer = data.data;
return dataBuffer;
});
};
})();
getData().then(..etc etc etc...);
As a final note, remember that you can use $http's built-in caching features, and not have to reinvent the wheel with your own buffers:
// much simpler, isn't it?
var getData = function() {
$http.get('/some/url/', { cache: true }) // enable caching
.then(function (data) { return data.data });
};
getData().then(...etc etc etc...);
Why dont you enable cache instead of handling the buffer manually.
$http.get('/some/url/',{ cache: true})
.success(function(data) {
deferred.resolve(data);
})
.error(function(data) {
deferred.reject();
});

Categories

Resources