Async Code to execute sequentially - javascript

i am facing an issue. i have angularjs function which in turn call to another angularjs function which has post request.This post request always fire at last when first function ends..it is not fired sequntially.
servicePOST.send(appConstants.BASE_MS_URL + 'Dcrs/activityDay.php',{
"date":d
}).then(function(result) {
console.log(result);
});
Please someone explain me this behaviour..any workaround for this...i want to execute all http request sequentially.how do i implemet it to this code? Thanks in advance!

This can help you
var TestService = function( $scope, servicePOST, AnotherService2, AnotherService3 )
{
// Level 1
servicePOST
.send(appConstants.BASE_MS_URL + 'Dcrs/activityDay.php',{
"date":d })// Request #1
.then( function( result ) // Response Handler #1
{
$scope.dcrlocked = result.dcrlocked;
// Level 2
AnotherService2 //AnotherService Call
.send({}) // Request #2
.then( function( result ) // Response Handler #2
{
$scope.leaves = result.leaves;
// Level 3
AnotherService3
.send({}) // Request #3
.then( function( result ) // Response Handler #3
{
//$scope.holidays = result.holidays;
});
});
});
};

As I understand from your question, you have a page where one section depends on other section and so on. and all these sections are being served/rendered by different-2 http request.
In this case, you can make the second http request from the success/resolved callback of first http request and so on. e.g.
servicePOST.send(appConstants.BASE_MS_URL + 'Dcrs/activityDay.php',{
"date":d
}).then(function(result) {
$scope.dcrlocked = result.dcrlocked;
$scope.leaves = result.leaves;
//$scope.holidays = result.holidays;
//make another http request as below.
servicePOST2.send(url,{data or data from last request}).then(function(){
// make another http request. and so on.
})
});
As all the http requests are being made from the success callback of last http request, it will guarantee sequential http requests.
EDIT
you can make use of $promise in your second function, where you are calling post request. e.g.
var deferred = $q.defer();
servicePOST.send(appConstants.BASE_MS_URL + 'Dcrs/activityDay.php',{
"date":d
}).then(function(result) {
$scope.dcrlocked = result.dcrlocked;
$scope.leaves = result.leaves;
//$scope.holidays = result.holidays;
deferred.resolve(result);
});
return deferred; // return deferred from your function.
Don't forget to inject $q in you controller and then passing it to second function. This will make post function to return synchronously. Let me know if this what you are looking for.

In short you can't. Javascript is non-blocking by design, you need to take a look at promises or implement nested callbacks.

Related

Node JS, chaining variable number of http requests

I'm using request js library to make HTTP requests to my API. My single API call looks like this:
var options = {
method: "post",
url: 'http//example.com',
json: true,
headers: headers,
body: {key: value}
}
request(options, callback);
However, I have array of options, which are needed to be called one after another and I need to break whole chain if one of them fails.
If last chain finishes, I need to output result to console.
I know that chaining callbacks could be fulfilled via promises, but all examples that I have found uses predefined amount of chained requests.
Is it possible?
A recursive function which calls itself in the request callback should work.
options = [{...}, {...}];
function doRequests(options){
request(options.shift(), function(){
if(error){
return "handle error";
}
if(options.length > 0){
doRequests(options);
}
});
}
The first thing I would do would be to use a request library that returned a promise. Assuming you have such a thing then you just chain the promises together.
First create a resolved promise:
var promise = new Promise.resolve();
The for each new object you want to request:
promise = promise.then(() => requestToPromise(options));
will chain another request onto the existing promise and will fire off a new request only when the previous one has completed.
If you have an array, you can have an index into that array, and have the callback kick off the next request when the previous one finishes. Roughly:
var index = 0;
var options = [/*...array of options objects...*/];
doRequest() {
request(options[index], function(err, result) {
// ...handle the result/error here. If there's no error, then:
if (++index < options.length) {
// Kick off the next request
doRequest();
}
});
}
While the above can be Promise-ified, since your requestmethod appears not to be, it would just complicate things.
You can instead use request-promise
and do the following
import request = require('request-promise');
var options = [/*...array of options objects...*/];
requests = [];
options.forEach(function(option){
requests.push(request(option));
}
Promise.all(requests).then(function(reponses){
//all requests are done.
});

How to call $http in angularJS sequentially? But I need to call it second $http based on response of first $http

I have to make two $http call but the second call must be based on first response(only if there is any error).
The best option is to use .then() with your request. When using .then, you can pass in multiple functions that will be called - the first if it is successful, the second if there is an error. So, it would look like this:
//First http call
$http.get('http://your-url.com')
.then(function (trsponse) {
//Call was successful; do something
}, function (response) {
//Call was unsuccessful; Grab whatever you need from the response and make your second http call:
var data = response.data;
var statusCode = response.status;
var statusText = response.statusText;
$http.get('http://your-second-url.com');
});
The response that gets passed into your functions will have these properties on it:
data – The response body
status – The status code of the response (e.g. 404)
headers – Headers on the response
statusText – The text of the status of the response
You can do something like this:
var promise1 = $http.get('firstUrl.com');
promise.then(
function(payload) {
$scope.movieContent = payload.data;
})
.catch(function (error) {
secondUrl();
console.log("Something went terribly wrong.");
});
Then :
var secondUrl = function(){
var promise2 = $http.get('secondUrl.com');
promise2.then(
// get the data and also other required functions
);
}

Webworker return result after callback

I have an Angular service where I'm using $q service in combination with webworkers. In my original function before using webworkers, my completeClass function would return an object.
I replaced the code to post a message to my new web worker script.
The callback of the webworker is in my initWorkers function where I add the eventlistener.
My goal is that the completeClass function returns the result of the webworker. How can I make this happen?
this.classWorker = new Worker('app/shared/autocomplete/autocomplete-class-worker.js');
this.completeClass = function(text){
var self = this;
var defer = $q.defer();
classWorker.postMessage([text, this.oldText, this.oldProposals, this.aliases, this.entityClasses])
};
this.initWorkers = function(){
var self = this;
worker.addEventListener('message', function(e) {
defer.resolve(e.data);
self.oldProposals = e.data[1];
self.oldText = text;
return e.data[0];
}, false);
};
If you are going to call the worker when the previous call is still running, then you need something to either queue or keep track of in-progress requests. My suspicion is that unless you need control of the queue of requests, it's simpler for UI thread to fire off the requests to the worker, so the browser essentially queues the requests for you.
But you would still need to keep track of the requests sent somehow, so when you get a message back from the worker, you know which one it is responding do. You can do this by, in the main thread
Generating a unique ID for each request. An ever-increasing integer can be enough for a lot of cases.
Creating a deferred object and storing it, associated with the ID
Firing off the request to the worker, passing the ID
Passing a the promise of the deferred object back to the caller
The worker then
Receives the message, with the ID
Does its' work
Posts the result back, along with the ID
The main thread then
Receives the message, with the ID and result of the work.
Retrieves the deferred object by the ID, and resolves it with the results of the work.
To do this, you can use some code like below. I've slightly simplified your case by passing just text to the worker, getting completeText back. You can add more information going either way in a real case.
app.service('AutoComplete', function($q) {
var id = 0;
var worker = new Worker('app/shared/autocomplete/autocomplete-class-worker.js');
var inProgress = {};
this.completeClass = function(text) {
var deferred = $q.defer();
id++;
var request = {
id: id,
text: text
};
inProgress[id] = deferred;
worker.postMessage(request);
return deferred.promise;
};
worker.onmessage = function(e) {
var response = e.data;
var id = response.id;
var type = response.type; // resolve, reject, or notify
var completeText = response.completeText;
inProgress[id][type](completeText);
if (type === 'resolve' || type === 'reject') {
delete inProgress[id];
}
};
});
Then in the worker you can have code like:
self.onmessage = function(e) {
var request = e.data;
var text = request.text;
var id = request.id;
// Do the work here
var completeText = ...
var response = {
id: id,
type: 'resolve', // Can reject the promise in the main thread if desired
completeText: completeText
};
self.postMessage(response);
};
My goal is that the completeClass function returns the result of the webworker. How can I make this happen?
To clarify, it can't directly return the result, because the result is calculated asynchronously, and all function calls must return synchronously. But it can return a promise that resolves to the result later, just like $http does for example. To use the above, you can do something like
app.controller('MyController', function($scope, AutoComplete) {
$scope.complete = function(text) {
AutoComplete.completeClass(text).then(function(result) {
// Do something with result
});
});
});
Note: technically, passing an ID along with each request isn't necessary if the worker does all its' work synchronously on one request, and so responds to each call in the order received. In the above example, the main thread can always assume the calls to the worker make a first-in-first-out queue. However, passing an ID gives the flexibility of the worker not finishing the work in the order received. Say in a later version it needs to do something asynchronous, like call another worker, or make an ajax request, this method will allow that.

New $.Deferred object with the old callbacks

Please forgive me if this is a stupid question. I have been trying for hours and my brain have just stopped working.
I have such system that consists of three AJAX calls. Server response of first call usually is a 200 Success; but second and third queries are fragile because they are image uploading, and on the server side, I have so much validation rules that client's images mostly fail.
window.AjaxCall = function () {
// to pass to $.ajax call later
this.args = arguments;
// xhr status
this.status = null;
// xhr results (jqXHR object and response)
this.xhrResponse = {};
this.dfr = new $.Deferred();
// to provide an easier interface
this.done = this.dfr.done;
this.fail = this.dfr.fail;
this.then = this.dfr.then;
};
AjaxCall.prototype.resetDfr = function () {
this.dfr = new $.Deferred();
};
AjaxCall.prototype.resolve = function () {
this.dfr.resolve(
this.xhrResponse.result,
this.xhrResponse.jqXHR
);
this.resetDfr();
};
AjaxCall.prototype.reject = function () {
this.dfr.reject(
this.xhrResponse.jqXHR
);
this.resetDfr();
};
AjaxCall.prototype.query = function () {
var _this = this;
// if query hasn't run yet, or didn't return success, run it again
if (_this.status != 'OK') {
$.ajax.apply(_this, _this.args)
.done(function (result, textStatus, jqXHR) {
_this.xhrResponse.result = result;
_this.xhrResponse.jqXHR = jqXHR;
_this.resolve();
})
.fail(function (jqXHR) {
_this.xhrResponse.jqXHR = jqXHR;
_this.reject();
})
.always(function (a, b, c) {
var statusCode = (typeof c !== 'string'
? c
: a).status;
if (statusCode == 200) {
_this.status = 'OK';
}
});
}
// if query has been run successfully before, just skip to next
else {
_this.resolve();
}
return _this.dfr.promise();
};
AjaxCall class is as provided above, and I make the three consecutive calls like this:
var First = new AjaxCall('/'),
Second = new AjaxCall('/asd'),
Third = new AjaxCall('/qqq');
First.then(function () {
console.log('#1 done');
}, function() {
console.error('#1 fail');
});
Second.then(function () {
console.log('#2 done');
}, function() {
console.error('#2 fail');
});
Third.then(function () {
console.log('#3 done');
}, function() {
console.error('#3 fail');
});
var toRun = function () {
First.query()
.then(function () {
return Second.query();
})
.then(function () {
return Third.query()
});
};
$('button').click(function () {
toRun();
});
Those code are in a testing environment. And by testing environment, I mean a simple HTML page and basic server support for debugging.
Home page (/) always returns 200 Success.
/asd returns 404 Not Found for the first 3 times and 200 Success once as a pattern (i.e. three 404s -> one 200 -> three 404s -> one 200 -> three 404s -> ... ).
/qqq returns 404 Not Found all the time.
When I click the only button on the page, first query returns success and second fails as expected. When I click the button second time, first query skips because it was successful last time and second fails again, also as expected.
The problem here is:
before I used the resetDfr method because the dfr is alreay resolved or rejected, it doesn't react to resolve and reject methods anymore.
When I call the resetDfr method in the way I show in the example, dfr is able to get resolved or rejected again, but the callbacks of the old dfr are not binded with the new dfr object and I couldn't find a way to clone the old callbacks into the new dfr.
What would be your suggestion to accomplish what I'm trying to do here?
Promises represent a single value bound by time. You can't conceptually "reuse" a deferred or reset it - once it transitions it sticks. There are constructs that generalize promises to multiple values (like observables) but those are more complicated in this case - it's probably better to just use one deferred per request.
jQuery's AJAX already provides a promise interface. Your code is mostly redundant - you can and should consider using the existent tooling.
Let's look at $.get:
It already returns a promise so you don't need to create your own deferred.
It already uses the browser cache, unless your server prohibits HTTP caching or the browser refuses it only one request will be made to the server after a correct response arrived (assuming you did not explicitly pass {cache: false} to its parameters.
If making post requests you can use $.post or more generally $.ajax for arbitrary options.
This is how your code would roughly look like:
$("button").click(function(){
var first = $.get("/");
var second = first.then(function(){
return $.get("/asd");
});
var third = second.then(function(){
return $.get("/qqq");
});
});
The reason I put them in variables is so that you will be able to unwrap the result yourself later by doing first.then etc. It's quite possible to do this in a single chain too (but you lose access to previous values if you don't explicitly save them.
For the record - it wasn't a stupid question at all :)

How to use data returned by $http request for another request in same controller using angularjs?

I am making a function that makes a http GET request, and then uses part of that response to make another http GET request. However, the data being returned by the first http GET request is wrapped in a lot of unnecessary data (maybe its a promise object), but I just need the json component of it. How do I access the json data of my response in the controller? Here is my code.
$scope.doSearch = function() {
var upcResult = Restangular.one('upc',$scope.searchTerm).get()
//the upc returns an item, so i am trying to access that part of the json by using
upcResult.item, how if I console.log that it is undefined
$scope.doAnotherSearch = function() {
var itemResult = Restangular.one('item',upcResult.item).get();
}
You can use promise chain.
var upcResult = Restangular.one('upc',$scope.searchTerm).get();
upcResult.then(function (result) {
// call other http get
return Restangular.one('item',result.item).get();
}).then(function (result) {
//....
});
I don't know if in your case Restangular.one(/*...*/).get(); returns promise but you can wrap it with $q like:
var upcResult = Restangular.one('upc',$scope.searchTerm).get();
var deferred = $q.defer();
deferred.resolve(upcResult).then(function(){/*...*/});

Categories