I am trying to implement BFS in angularjs where for each node you make a Restangular call. The 'while loop' should be run as many times as the number of nodes in the graph. But it runs only 1 time. As after making the async call(and without executing the part where we add children nodes in queue), JS engine goes forward in the loop and exits the loop as length of queue is zero.
How can we stop the execution till async call is completed? Should we poll on whether the async call has completed or not?
angular.module('app', ['restangular']);
angular.module('app')
.controller('TestCtrl', ['$scope','Restangular', '$q',
function($scope,Restangular,$q)
{
var defer = $q.defer();
var queue = [];
queue.push(0);
//A set to track visited nodes
var visited = {};
while(queue.length != 0)
{
var item = queue.shift();
console.log("in while loop");
//for each unvisited node, make a rest call to get it's children, then add them to queue and mark that node as visited
if(!(item in visited)){
defer.promise.then(Restangular.one('/some/path/getChildren',item).get().then(function()
{
//This part of code is executed only 1 time.
console.log("made rest call for "+item);
//each item can have 0 or 1 or 2 children
queue.push(item.children);
visited[item]=true;
}
));
}
}
}
]);
You cannot do that in JS - the async call will get made after the function finishes. What you need to do is have a $q promise that you resolve as soon as there are no items in the queue. Your queue checking logic must be within the async callback - which may well need to reuse itself as callback for the next Restangular call.
Related
I am facing a weird case, I am using google directionsService.route. but it doesn’t sync well. Here is my code:
angular.forEach(requestArray, function(v, i) {
directionsService.route(v, function(result, status) {
var googleLeg = result.routes[0].legs[0];
// sth else...
});
});
As you can see, I am looping the location Array into the route. every time I fire the function, it will go through the requestArray first, (if we make a breakpoint on the line (var googleLeg = result.routes[0].legs[0]), it doesn’t reach there until it goes through all the requestArray.(i from 0 - length); then it will have the second loop for directionsService.route( at this time, it will reach to line(var googleLeg = result.routes[0].legs[0]); Any idea about this?
Essentially your problem is that calling a google service is an asynchronous call, and you are not guaranteed when the callback will execute. If you need to process requestArray synchronously, here is what you can do:
function start() {
// create a copy of request array
var stuff = [].slice.call(requestArray);
function continueSync() {
// stop the recursion if we have nothing left to process
if (!stuff || stuff.length == 0) return;
// grab the first item off of the stuff queue
v = stuff[0];
stuff = stuff.slice(1);
// call to google
directionsService.route(v, function(result, status) {
var googleLeg = result.routes[0].legs[0];
// sth else...
// now continue processing the rest of the stuff queue through tail recursion
continueSync();
});
}
// kick off our recursive processing
continueSync();
}
your problem is that calling a google service is an asynchronous call, you could prove to generate threads
I am new to using of web worker API in angularjs, I want to know the flow sequence for the web worker in angularjs, i.e., should I write services, then use them in controller and so on.. please i want to know flow sequence...
Yes, the question is very general.
But for those, who need to implement a particular task in angular environment I can recommend plugin https://github.com/vkiryukhin/ng-vkthread It let you easily export a function in a thread, execute it and get result in UI.
something like this:
/* function to execute in a thread */
function foo(n, m){
return n + m;
}
/* create an object, which you pass to vkThread as an argument*/
var param = {
fn: foo // <-- function to execute
args: [1, 2] // <-- arguments for this function
};
/* run thread */
vkThread.exec(param).then(
function (data) {
console.log(data); // <-- thread returns 3
},
function(err) {
alert(err); // <-- thread returns error message
}
);
see live demo at: http://www.eslinstructor.net/ng-vkthread/demo/
contact me on github if you have any questions
I am trying to call a loopback find function inside of a for loop, passing in a value from the iteration into the loopback function. The main issue of the code can be represented by the following:
for (var a = 0; a < $scope.countries.length; a++) {
$scope.getEmFacPurElec($scope.countries[a], 'ton/kWh', 'CO2e').then(function(result) {
emFacPurElecToUse = $scope.emFacPurElecs;
}
And here is the function being called:
$scope.getEmFacPurElec = function (country, unit, ghgType) {
var defer = $q.defer();
$scope.emFacPurElecs = [];
$scope.emFacPurElecs = Country.emFacPurElecs({
id: country.id,
filter: {
where: {
and: [
{unit: unit},
{ghgType: ghgType}
]
}
}
});
defer.resolve('Success getEmFacPurElec');
return defer.promise;
};
The problem is that the loopback promise function is called and then returned undefined which means that it moves to the next iteration of the for loop before getting the value to assign to emFacPurElecToUse. I need to do some more calculations with that variable for that country before moving to the next country.
I have looked at using $q.all as a possible solution and also using array.map as per http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html (Rookie mistake #2: WTF, how do I use forEach() with promises?), but I just cannot figure out how to pull it all together to make it work. Should I be using a forEach instead?
I also saw this link angular $q, How to chain multiple promises within and after a for-loop (along with other similar ones) but I do not have multiple promises that I need to process inside the for loop. I need to retrieve the value of one emFacPurElecs for that country, do some work with it, then move to the next country. I feel I am close but I just cannot get my head around how I would code this particular functionality. Any help is greatly appreciated.
It seems to me that you do have multiple promises to process inside your for loop, as you say "I need to do some more calculations with that variable for that country before moving to the next country." This should all be done with in the promise chain that I've suggested - calcEmFacPurElec.
$scope.calcEmFacPurElec = function (country, unit, ghgType) {
$scope.getEmFacPurElec(country, unit, ghgType).then(function(countryEmFacPurElecs) {
// do something with countryEmFacPurElecs
return countryEmFacPurElecs;
}
$scope.getEmFacPurElec = function (country, unit, ghgType) {
var defer = $q.defer();
defer.resolve(Country.emFacPurElecs({
id: country.id,
filter: {
where: {
and: [
{unit: unit},
{ghgType: ghgType}
]
}
}
}); );
return defer.promise;
};
Hopefully the above is a pointer in the right direction!
When you want to carry out a promise chain on an array of items, then as you have identified, Promise.all (using whatever promises implementation you require) is what you want. .all takes in an array of Promises, so in your for loop you can do:
var promises = [];
for (var a = 0; a < $scope.countries.length; a++) {
promises.push($scope.calcEmFacPurElec($scope.countries[a], 'ton/kWh', 'CO2e')); // new promise chain that does all of the work for that country
}
$q.all(promises).then(function(arrayofCountryEmFacPurElecs) {console.log('all countries completed')});
I am new to protractor and currently experimenting with an internal Angular JS application. In the below snippet of code, I am unable to understand on how Protractor is executing the statements and function calls. The behaviour seems weird OR I am doing something completely wrong. Appreciate all the help.
describe('Describe Function', function()
{
it('Should return a value',function(){
var IndexValue = ' ';
var col_model = element.all(by.repeater('col in renderedColumns'));
var row_model = element.all(by.repeater('row in renderedRows'));
browser.get('URL');
ptor = protractor.getInstance();
var user = element(by.model('login.user_id_1'));
user.sendKeys('demo');
element(by.id('txtusername')).getAttribute('value').then(function(text) {
console.log(text); // This line prints 'demo'
});
var pwd = element(by.model('login.password_1'));
pwd.sendKeys('demo');
var submit = element(by.className('login-submit'));
submit.click();
browser.driver.sleep(1000);
var colCount = element.all(by.repeater('col in renderedColumns')).count();
colCount.then(console.log) // This prints '80'
var items = [];
getArrayList();
function getArrayList() // Function to capture all the content of the table in an array
{
console.log('Array List function started');
colCount.then(function(Col_count){
for(var i=0; i < Col_count; ++i){
var grid = browser.findElement(by.repeater('col in renderedColumns').row(i));
grid.getText().then(function(gridValue){
items.push(gridValue.trim());
console.log('For loop')
});
}
});
console.log('Array List function completed');
}
console.log(items); // This prints []
getGridValue('Prospect');
function getGridValue(name)
{
console.log('Inside grid value');
}
});
});
When I execute the above code, even before the browser is invoked and the application is launched, first 4 lines (specified below) are printed on the console. What surprises me is the fact that point 1 and 3 are part of the function call which indeed is printing point 7. Looks like all the independent "console.log" are executed first and then the "console.log" associated with "element." statements are executed. Its really confusing. Plz let me know where am I going wrong. Thanks.
OUTPUT ON THE CONSOLE
1. Array List function started
2. []
3. Array List function completed
4. Inside grid value
5. demo
6. 80
7. For loop
For loop
For loop
For loop
.... so on until the loop ends
At first: well, protractor does, what you told it to do... You may read a bit about promises and async JavaScript.
I try to explain a bit, please understand, that explaining everything would take a lot of time.
Starting protractor calls your script (spec). The most lines you wrote before calling getArrayList() are function calls which return promises. This means, the function is called, and when ready, it's callback function gets called (e.g.: then(...))
An example:
var colCount = element.all(by.repeater('col in renderedColumns')).count();
colCount.then(console.log) // This prints '80'
In this two lines you are searching the DOM for every element which can be located by col in renderedColumns, then, if ready, count them and return the value to its callback then, afterwards print it via console.log.
But all of this takes time, and this is the reason why console.log('Array List function started'); gets printed before colCount.then(console.log).
Hope I could help a bit, as mentioned before, you may read a bit about promises.
I'm using angular-ui-tree to make a scorecard application that has a hierarchy of scorecard nodes. My scorecard nodes are persisted through a web service layer and have their own ID numbers to identify them.
In order to work with a scorecard node by ID, I implemented the following method:
// get a node scope by a Scorecard Node ID.
function getNodeScopeByNodeID(id, nodesScope) {
nodesScope = nodesScope || getRootNodesScope().$nodesScope;
var nodes = nodesScope.childNodes();
for (var i = 0; i < nodes.length; i++) {
var result;
if (nodes[i].$modelValue.ID == id)
result = nodes[i];
else {
var subScope = nodes[i].$childNodesScope;
if (subScope) {
result = getNodeScopeByNodeID(id, subScope);
}
}
if (result)
return result;
}
return undefined;
}
The details here aren't super important except to say that this method crawls the nested scopes that make up the tree until it finds a scope whose $modelValue (my scorecard node state) has the ID we're looking for. It works great, except when I do something like this:
// add a new objective node to the root of the scorecard.
$scope.newObjective = function () {
var scope = getRootNodesScope();
var rootArray = scope.$nodesScope.$modelValue;
scorecardService.addObjective($scope.selectedScorecard, rootArray)
.then(function (d) {
rootArray.push(d);
$scope.edit(getNodeScopeByNodeID(d.ID));
})
.catch(errorHandler);
}
Note the two lines in the "then" function. I'm asking my service to create a new node, and when I get it I'm pushing it into an array that is part of the model. I want my newly inserted node to immediately flip into "edit" mode (which reveals some form controls inside it.) But the call to getNodeScopeByNodeID immediately following the push fails. If I debug it it looks like the scope hasn't caught up with the model yet. I have one more node in my node array (model) than there are child scopes where I expect them to be.
I think I just have to give angular a chance to react to the model change before I start going through the scope to find what I'm looking for. How do I do that?