directionsService.route doesn't sync well - angularjs

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

Related

Make a while loop, loop for 1 second in Dart/Flutter

I am trying to make a while loop loop a statement exactly for one second after which it stops. I have tried this in DartPad, but it crashes the browser window.
void main(){
var count = 0.0;
bool flag = true;
Future.delayed(Duration(seconds: 1), (){
flag = false;
});
while (flag){
count++;
}
print(count);
}
Am I doing something wrong?
I like how you are trying to figure Futures out. I was exactly where you were before I understood this stuff. It's kind of like threads, but quite different in some ways.
The Dart code that you wrote is single threaded. By writing Future.delayed, you did not start a job. Its execution won't happen unless you let go of the thread by returning from this main function.
Main does not have to return if it is marked with async.
Two actions have to run "concurrently" to be able to interact with each other like you are trying to do. The way to do it is to call Future.wait to get a future that depends on the two futures. Edit: Both of these actions have to let go of execution at every step so that the other can get control of the single thread. So, if you have a loop, you have to have some kind of await call in it to yield execution to other actions.
Here's a modified version of your code that counts up to about 215 for me:
Future main() async {
var count = 0.0;
bool flag = true;
var futureThatStopsIt = Future.delayed(Duration(seconds: 1), (){
flag = false;
});
var futureWithTheLoop = () async {
while (flag){
count++;
print("going on: $count");
await Future.delayed(Duration(seconds: 0));
}
}();
await Future.wait([futureThatStopsIt, futureWithTheLoop]);
print(count);
}

For-loop isn't returning the items in an array in the correct order

I have an array of Integers that I need to loop through that then make a network request for further information and with the returned information populate an array of a new object I have created.
I want the returned data to come back in the same order in which the array provides it in, however it is coming back in a different order and am assuming it might have something to do with the network request.
I'm new(ish) to development, so the answer may be very obvious, but I'm really at a dead end with what to do next.
I've tried adding delays to the network request on each loop, i've tried calling .sort() on the array to ensure the array stays in the correct order
var tacticalCoverIdArray = [Int]()
var savedTacticalCoverData = [Covers]()
for coverID in tacticalCoverIdArray {
performGetRequestForSpecificCovers(coverID: coverID, targetURL: targetURL, completion: { (data, HTTPSatusCode, error) -> Void in
if HTTPSatusCode == 200 && error == nil {
do {
if coverID != 0 {
let decodedJSON = try JSONDecoder().decode([Covers].self, from: data)
savedTacticalCoverData.append(decodedJSON[0])
} else {
let data = Covers(id: 0, game: 0, image_id: "")
savedTacticalCoverData.append(data)
}
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
saveTacticalCoverData()
})
} catch let error {
print("Error decoding JSON: \(error)")
}
} else {
print("HTTP status code error: \(HTTPSatusCode)")
print("Error loading data: \(String(describing: error))")
}
})
}
When putting a print statement under the very first declaration of the for-loop (i.e: print(coverID) the return is what I would expect, in which it loops through each integer and then returns them in order.
However, as soon as I put the same print statement under the 'performGetRequestForSpecificCovers' method, the coverID array is not in the order that it should be in, and therefore I get my returned values in an incorrect order when I append them to my 'savedTacticalCoverData' array.
Your hunch about the network requests having an impact on the ordering seems correct.
What I'm guessing is happening here is that when you are looping over tacticalCoverIdArray and calling performGetRequestForSpecificCovers(), that loop doesn't wait for that network request to complete and for the completion block to get called. It continues with the next iteration. Effectively, you are sending tacticalCoverIdArray.count network requests in parallel. Those completion blocks get called much later, long after the outer loop is complete, and most likely even on a different thread.
The most basic, and worst, option is to use DispatchSemaphore to hold up the outer loop until the completion block is called. You'd create a semaphore, call semaphore.signal() inside the completion handler, and call semaphore.wait() at the end of every loop iteration. The problem with this approach is that you will wait for each network request to complete before proceeding to the next one. Also, you will tie up the thread that is executing the first outer loop, and threads are a finite resource, so it's not a good idea to waste them.
A better option is to dispatch all requests at once, and handle the out-of-order responses. This will complete much faster than serially dispatching them, unless you encounter some kind of limitations with dispatching so many network requests in parallel. Instead of savedTacticalCoverData being an array, perhaps it could be a dictionary, where the key is the index of the outer loop, and the value is what you're trying to save? Each time the completion handler gets called, you could check whether or not the dictionary is full and you've accumulated all of the responses you want, and only then proceed with the final "everything is done" action, presumably saveTacticalCoverData().
You'll have to be careful to get your multithreading right. Unless performGetRequestForSpecificCovers() uses only one callback queue, and it's the same queue that this function is running on, you might get called on different threads. If that's the case, I would recommend making a new DispatchQueue and always operating on your dictionary only from that queue, to ensure consistency when those completion blocks come in on random threads. Something like this:
class MyClass {
var tacticalCoverIdArray = [Int]()
var savedTacticalCoverData = [Int: Covers]()
var queue = DispatchQueue(label: "Class Internal Queue")
func myFunc() {
// ... fill in the blanks here
for (index, coverID) in tacticalCoverIdArray.enumerated() {
performGetRequestForSpecificCovers(coverID: coverID, targetURL: targetURL, completion: { (data, HTTPSatusCode, error) -> Void in
if HTTPSatusCode == 200 && error == nil {
do {
queue.async {
if coverID != 0 {
let decodedJSON = try JSONDecoder().decode([Covers].self, from: data)
self.savedTacticalCoverData[index] = decodedJSON[0]
} else {
let data = Covers(id: 0, game: 0, image_id: "")
self.savedTacticalCoverData[index] = data
}
if self.savedTacticalCoverData.count == self.tacticalCoverIdArray.count {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
self.saveTacticalCoverData()
})
}
}
} catch let error {
print("Error decoding JSON: \(error)")
}
} else {
print("HTTP status code error: \(HTTPSatusCode)")
print("Error loading data: \(String(describing: error))")
}
})
}
}
}

_.each wait till boolean condition has been met to loop around

How can I achieve a loop like this:
foobar.each(function (model, j) {
// asynchrounous call etc. {in here bool get set to true}
// outside all asynchronous calls
// wait till bool is true, without stopping anything else except the loop to the top of
the _.each
})
I asked a similar question yesterday. But it got marked as a duplicate when it wasn't the same case. Their solution did not achieve the same thing. Also generator functions were suggested which looked like it would work. But I can't use them with ecmascript 5
I've tried busy loops and set time out but they don't seem to work either
I've also tried this:
goto();
function goto() {
if (foo === true) {
//return true; /*I've tried with and without the return because the loops
doesn't need a return*/
} else {
goto();
}
}
What happens with the goto() method is it breaks. Giving me the right results for the first iterations then execution seems to stop altogether. 'foo' always gets set to true in normal execution though.
What you could do is implement a foreach yourself, where you execute your condition, and then on success callback go to the next item (but meanwhile the rest of the code will keep running.
var iteration = 0 //count the iteration of your asynchronous process
//start looping
loop(iteration)
function loop(iteration){
var model = foobar[iteration];
//exit your loop when all iterations have finished (assuming all foobar items are not undefined)
if (foobar[iteration] === undefined){
return;
}
//do what you want
//on success callback
iteration++;
loop(iteration);
//end success callback
}

How to use html webworkers with angularjs?

I am handling very hight dataset with type arry. While iterating through this arrays my application gets performance hit. I want to use webworkers which will perform manipulation operations on huge array and it will directly return the processed dataset back to application.web
Take a look at https://github.com/vkiryukhin/ng-vkthread I develop it exactly for such kind of tasks. It let you easily export function in a thread, execute it and get result in UI. Even more: you can download data directly in a thread rather than transfer it from UI to a thread.
the very basic usage is:
/* 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
}
);
Doc and examples: http://www.eslinstructor.net/ng-vkthread/demo/

Breadth First Search in Angularjs with async calls

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.

Resources