I need to send multiple get requests(required) one by one. When the count is 2-3, it works fine, but with almost 6 HTTP Get requests, sometimes some of them fails and give Internal Sever Error(500). Opening the error link in new tab gives required results.
So there is nothing wrong from server side.
I'm facing this problem both in : localhost and production.
How to deal with this situation from client side?
I've tried:
NodeJS + SocketIO to send data from server without asking. [with so much data if socket keeps writing till 60 sec. socket re-registers & restarts from beginning.]
Angular + NGResource. [internally uses http get. issue persists.]
Angular + Restangular Lib. [internally uses http get. issue persists.]
Please suggest how do I know what the problem is. Then only I can think of a solution.
Thnx!!
Here's a function in which you can wrap your HTTP calls. It will repeat the call until it passes. Beware! If the HTTP call fails 100% of the time (for example, malformed URL), then the function will not stop (In testing, the function was called >70,000 times. Apparently there is no recursion limit with promises). For that case, I've included a limited version of the function that stops after n attempts.
var persistentRequest = function(requestFn) {
var deferred = $q.defer();
requestFn().then(function() {
deferred.resolve();
}, function() {
persistentRequest(requestFn).then(
function() {
deferred.resolve();
}
);
});
return deferred.promise;
}
var persistentRequestLimited = function(requestFn, n) {
var deferred = $q.defer();
if (n <= 0) {
deferred.reject('Did not complete in given number of tries');
} else {
requestFn().then(function(data) {
deferred.resolve(data);
}, function() {
persistentRequestLimited(requestFn, n-1).then(
function(data) {
deferred.resolve(data);
},
function(rejection) {
deferred.reject(rejection);
}
);
});
}
return deferred.promise;
}
For example, use it like:
persistentRequest(function() {
return $http.get('/myurl');
});
persistentRequestLimited(function() {
return $http.get('/myurl');
}, 10);
Don't forget to inject $q into your controller/service/etc.
Related
So I have a situation where I need to perform a bunch of http calls, then once they are complete, continue on to the next step in the process.
Below is the code which does this and works fine.
However, I now need to wait a few seconds between each of the http calls. Is there a way to pass in a timeout with my current set up, or will it involve a good bit of refactoring?
Can post more code if needs be. I have tried passing in a timeout config varable into the http call, however, they still get fired at the same time.
Any advice would be great.
Code
var allThings = array.map(function(object) {
var singleThingPromise = getFile(object.id);
return singleThingPromise;
});
$q.all(allThings).then(function() {
deferred.resolve('Finished');
}, function(error) {
deferred.reject(error);
});
Instead of using $q.all, you might want to perform sequential calls one on success of previous and probably with use of $timeout. Maybe you could build a recursive function.
Something like this..
function performSequentialCalls (index) {
if(angular.isUndefined(array[index])) {
return;
}
getFile(array[index].id).then(function() {
$timeout(function() {
performSequentialCalls(index + 1)
}, 1000) // waiting 1 sec after each call
})
}
Inject required stuff properly. This assumes array to contain objects with ids using which you perform API calls. Also assumes that you are using $http. If using $resource, add $promise accordingly.
Hope that helps a bit!
function getItemsWithDelay(index) {
getFile(object[index].id).then(()=>{
setTimeout(()=>{
if(index+1 > object.length) { return }
getItemsWithDelay(index+1)
}, 5000)
})
}
You can make sequential calls
This is a awesome trick question to be asked in an interview, anyways I had a similar requirement and did some research on the internet and thanks to reference https://codehandbook.org/understanding-settimeout-inside-for-loop-in-javascript
I was able to delay all promise call in angularjs and the same can be applied in normal JS syntax as well.
I need to send tasks to a TTP API, and they requested to add a delay in each call
_sendTasks: function(taskMeta) {
var defer = $q.defer();
var promiseArray = [];
const delayIncrement = 1000 * 5;
let delay = 0;
for (i = 0; i < taskMeta.length; i++) {
// using 'let' keyword is VERY IMPORTANT else 'var' will send the same task in all http calls
let requestTask = {
"action": "SOME_ACTION",
"userId": '',
"sessionId": '',
};
// new Promise can be replaced with $q - you can try that, I haven't test it although.
promiseArray.push(new Promise(() => setTimeout(() => $http.post(config.API_ROOT_URL + '/' + requestTask.action, requestTask), delay)));
delay += delayIncrement;
}
$q.all(promiseArray).
then(function(results) {
// handle the results and resolve it at the end
defer.resolve(allResponses);
})
.catch(error => {
console.log(error);
defer.reject("failed to execute");
});
return defer.promise;
}
Note:: using 'let' keyword in FOR loop is VERY IMPORTANT else 'var' will send the same task in all http calls - due to closure/context getting switched
Lets say that we have two buttons, each on are calling the following method:
var NUMBER_OF_IMAGE_REQUEST_RETRIES = 3;
var IMAGE_REQUEST_TIMEOUT = 3000;
processImage: function(image_data) {
var main_response = $q.defer();
var hash = getImageHash(image_data);
var requestsCounter = -1;
requestImage = function() {
$http.post(apiUrl, {params: {data: hash},timeout: IMAGE_REQUEST_TIMEOUT})
.then(function(response) {
return main_response.resolve(response.data);
}, function(error) {
if (++requestsCounter < NUMBER_OF_IMAGE_REQUEST_RETRIES) {
requestLabelsImage();
} else {
return main_response.reject();
}
});
};
requestLabelsImage();
return main_response.promise;
}
The method passes an image related data to the server, the server process the data and then response. Every time a user press a different button different image_data is being send to the server.
The problem:
The user press button 1, the method is called with image_data_1, and then he/she immediately press button 2 and the method is called with image_data_2. The processImage function is called by another method, lets say doSomethingWithTheResponse which only cares about the latest user's action, but the image_data_2 is proceed faster by the servers, so the client gets image_data_2 before image_data_1, so the client believes that image_data_1 was related to the user's latest action, which is not the case. How can we ensure that the client is always getting the response that is related to the users latest action?
Note: The hash is different for the differente image_data requests.
I was thinking something like:
var oldhash = null;
processImage: function(image_data) {
var main_response = $q.defer();
var hash = getImageHash(image_data);
oldhash = hash;
var requestsCounter = -1;
requestImage = function(hash) {
if(hash === oldhash){
$http.post(apiUrl, {params: {data: hash},timeout: IMAGE_REQUEST_TIMEOUT})
.then(function(response) {
return main_response.resolve(response.data);
}, function(error) {
if (++requestsCounter < NUMBER_OF_IMAGE_REQUEST_RETRIES) {
requestLabelsImage(hash);
} else {
return main_response.reject();
}
});
}
else {
main_response.reject();
}
}
requestLabelsImage(hash);
return main_response.promise;
}
But I am not 100% sure that this is the right approach.
Simply disregard the previous requests.
You can create a repository of requests (array or dictionary implementation is okay). Call .abort() on the previous ones once another request is made -- when you add it in your storage.
If you want a dictionary, there is a good example here (tackles a different topic, though), but here is a modified snippet of his code which is related to your case:
var _pendingRequests = {};
function abortPendingRequests(key) {
if (_pendingRequests[key]) {
_pendingRequests[key].abort();
}
}
Where the key can be.. say... a category of your action. You can name constants for it, or it can be just the name of the button pressed. It can even be a URL of your request; completely up to you.
There is an excellent explanation of the whole concept here:
jquery abort() ajax request before sending another
https://stackoverflow.com/a/3313022/594992
If your UI allows for initiation multiple actions, while processing of those actions are mutually exclusive, then you should probably use promises, and track active promises.
button1.addEventListener("click", function(evt) {
startRunning( task1.start() );
});
button2.addEventListener("click", function(evt) {
startRunning( task2.start() );
});
With a task runner like:
function startRunning( promise ) {
while(runningTasks.length>0) {
cancel( runningTasks.unshift() );
});
runningTasks.push( promise );
}
Your cancel function can come from anything that can deal with promises, like Angular's service.cancelRequest, or you can write your own code that takes the promise and smartly breaks off its operation.
Of course, if you're not using promises, then you probably want to start doing so, but if you absolutely can't you can use a manager object like:
button1.addEventListener("click", function(evt) { task1(); });
button2.addEventListener("click", function(evt) { task2(); });
with
var manager = [];
function cancelAll() {
while(manager.lenght>0) {
var cancelfn = manager.unshift()
cancelfn();
}
return true;
}
function task1() {
var running = cancelAll();
manager.push(function() { running = false; });
asyncDo(something1, function(result) {
if(!running) return;
// do your real thing
});
}
function task1() {
var running = cancelAll();
manager.push(function() { running = false; });
asyncDo(something2, function(result) {
if(!running) return;
// do your real thing
});
}
And you can put cancels on as many aspects as you need. If you need to cancel running XHRs, you might be able to do so, if you have multiple steps in your result handling, cut off at each step start, etc.
This sounds like an ideal use-case for promises. Basically, whenever a new request is made, you want to cancel any existing promises. I am not versed in AngularJS, but the following ng-specific links might prove useful:
Angularjs how to cancel resource promise when switching routes
Canceling A Promise In AngularJS
Simply put, I'm trying to dynamically generate an AJAX request based off a scenario that I'm retrieving via an AJAX request from a server.
The idea is that:
A server provides a "Scenario" for me to generate an AJAX Request.
I generate an AJAX Request based off the Scenario.
I then repeat this process, over and over in a Loop.
The big idea is that I can change the second AJAX request dynamically, based off the Scenario given from the server.
I have this working, but I feel like the way I'm doing this is very messy. Is there any better way to go about thinking through this problem? Perhaps promises? If anyone could please review this and provide feedback or suggestions on how to clean it up--that would be greatly appreciated.
Here is my code: http://jsfiddle.net/6jph0e98/
(please open the console to see everything in action)
As a reference, here is the scenario data I'm currently working with:
var scenario = {
"base": {
"frequency": "5000"
},
"endpoints": [
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/alvarengarichard",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
}
]
}
Here are my 2 cents: http://jsfiddle.net/3Lddzp9j/6/.
Yes, I think you can do this more elegantly by chaining promises. So I figured out what I think your app does, and how you can do it by chaining these promises. What is interesting that certain steps already return promises ( the jQuery AJAX calls ) but others don't. For those - we have to create our own promise that instantly resolves. And then there was the timeout which we wrapped in a promise.
Also, I tried to use some JS best practices, like keeping things out of the global space by wrapping them in an IIFE and applying the module pattern.
This makes the overall control flow of your application nice and clean IMHO:
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
And also hides the private members and only exposes the run() method:
return {
// This will expose only the run method
// and will keep all other functions private
run : run
}
Hope it helps - let me know what you think. Here's the full source, with comments:
// First of all - I'm using the javascript module pattern here
// this will all be much more easy once ES6 it out, but this will
// have to do for now.
// Also, I'm importing jQuery into the module as you can see, which
// is wrapped inside the IIFE ( Google it ) which keeps things nicely
// out of the global scope.
var App = (function ($) {
// Gets the scenario from the API - $.get is just some syntactic
// sugar for $.ajax with GET as method - NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario');
};
// The result of the previous promise is passed into the
// next as we're chaining. So the data will contain the
// result of getScenario
var mapToInstruction = function (data) {
// We map it onto a new instruction object
var instruction = {
method: data.endpoints[0].method,
type: data.endpoints[0].type,
endpoint: data.endpoints[0].endPoint,
frequency: data.base.frequency
};
console.log('Instructions recieved:');
console.log(instruction);
// And now we create a promise from this
// instruction so we can chain it
var deferred = $.Deferred();
deferred.resolve(instruction);
return deferred.promise();
};
// This wraps the setTimeout into a promise, again
// so we can chain it
var waitForTimeout = function(instruction) {
console.log('Waiting for ' + instruction.frequency + ' ms');
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve(instruction)
}, instruction.frequency);
return deferred.promise();
};
// Final step: call the API from the
// provided instructions
var callApi = function(instruction) {
console.log('Calling API with given instructions ...');
return $.ajax({
type: instruction.method,
dataType: instruction.type,
url: instruction.endpoint
});
};
var handleResults = function(data) {
console.log("Handling data ...");
var deferred = $.Deferred();
deferred.resolve();
return deferred.promise();
};
// The 'run' method
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
return {
// This will expose only the run method
// and will keep all other functions private
run : run
}
})($);
// ... And start the app
App.run();
After too many hours, I cannot for the life of me get this example working. I'm following Ben Lesh's excellent guides to mocking $http requests in Angular, but for some reason the service request is not sending.
I have verified that the service is working properly by building a separate HTML DOM and applying my app and a tiny controller to it. I'm using Jasmine 1.3 and Angular 1.2.9 (and angular-mocks.js of course).
Here's the app:
var app = angular.module('myApp', []);
app.factory('httpBasedService', function($http) {
return {
sendMessage: function(msg) {
return $http.get('something.json?msg=' + msg)
.then(function(result) {
console.log(result.data)
return result.data;
});
}
};
});
And the test:
describe("httpBasedService ", function () {
var httpBasedService,
mockBackend;
beforeEach(function (){
module('myApp');
inject(function(_$httpBackend_, _httpBasedService_) {
mockBackend = _$httpBackend_;
httpBasedService = _httpBasedService_;
});
});
afterEach(function() {
mockBackend.verifyNoOutstandingExpectation();
mockBackend.verifyNoOutstandingRequest();
});
it('should send the msg and return the response.', function () {
var returnData = {excited: true};
mockBackend.expectGET('something.json?msg=wee').respond(returnData);
var returnedPromise = httpBasedService.sendMessage('wee');
var result;
returnedPromise.then(function(response) {
result = response;
});
mockBackend.flush();
expect(result).toEqual(returnData);
});
});
I'm getting a Error: No pending request to flush ! and of course a Error: Unsatisfied requests: GET (since nothing has been sent). I watch the network requests and sure enough, no request is sent, even if I remove the mock backend.
I've commented out stuff, restructured stuff, tried lots of other examples, but to no avail. Can someone help me?
As usual, when I talk to the duck, the problem is fixed.
Turns out I was using angular-mocks.js from v1.1.0. My angular.js version was 1.2.9.
Facepalm.
Check your versions, future readers, and save yourself a few hours.
I'm new to AngularJS, so please, bear with me. I got the following code:
var testModule = angular.module("testModule", ['ngResource']);
testModule.config(function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
'self',
'https://api.twitter.com/**']);
});
testModule.controller("SecondController",
function SecondController($scope, $resource){
$scope.secondField = "Hello World";
try{
var restClient =
//$resource("https://api.github.com/repos/angular/angular.js/issues", [],
$resource("https://api.twitter.com/1.1/search/tweets.json", [],
{
"get":{method:"GET", headers:{"Accept":"application/json"},
interceptor: {
"response":function(data){
alert("Response: " + data);
},
"responseError":function(data){
alert("Error: " + data);
}
}
}
});
$scope.about = restClient.get();
} catch(err) {
$scope.error = err.message;
}
$scope.done = true;
}
);
It's supposed to execute the responseError function and data.status should be equal to 215 (the request requires authentication to pass successfully). Instead, the response code is 0.
I'm actually trying to reach a different API (that does not require authentication), but the status code is also 0 and I don't really have any idea what the issue might be. The only successful request I could execute till now is:
GET https://api.github.com/repos/angular/angular.js/issues
Executed by $resource.query(). But nor twitter, nor the target API can be reached. I hope providing me a solution for the twitter example can help me solve my issue.
Thanks.
Edit: Using AngularJS 1.2.0 rc2
This may be because of cross domain request. Status code 0 will come when you try to access web service of different domain. In this case you need to use JSONP as method in ngResource.
http://docs.angularjs.org/api/ngResource.$resource