Consider the following code:
Loop::run(function() {
Loop::onSignal(SIGINT, function () use ($w) {
echo "Caught SIGINT! exiting ...\n";
Loop::stop();
});
while([$jobId, $jobData] = yield $beanstalk->reserve()) {
$response = yield $httpClient->send($request);
yield $beanstalk->delete($jobId);
}
});
To my understanding, when this code receives a SIGINT, the callback in Loop::onSignal will be executed on the next tick and thus the loop will stop and the program exit.
In this case, if $httpClient->send() in the while loop has not yet finished, the next line will not be executed and the Beanstalk message will not get deleted. The Beanstalk job will then be retried and that might cause a problem if the HTTP request is not idempotent. We can substitute an HTTP request with a database call.
Is there a way to tell the loop to not execute new generators and wait for the generators in-progress to finish when a SIGINT is received?
I've been trying to wrap my head around this but couldn't find a solution.
There's no such option, but you can achieve what you want in your simple application pretty easily by adding a flag.
Loop::run(function() {
$running = true;
Loop::onSignal(SIGINT, function () use (&$running) {
echo "Caught SIGINT! exiting ...\n";
$running = false;
});
while($running && [$jobId, $jobData] = yield $beanstalk->reserve()) {
$response = yield $httpClient->send($request);
yield $beanstalk->delete($jobId);
}
});
This will simply continue with the current request and exit afterwards.
Related
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);
}
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))")
}
})
}
}
}
I am attempting to put in a pause between a forEach loop for a list.
I would have thought the timeout would cause a pause for the loop but it just seems to start 3 timers all at once. (In very quick succession.)
startTimeout(int seconds) async {
print('Timer Being called now');
var duration = Duration(seconds: seconds);
Timer(duration, doSomething());
}
startDelayedWordPrint() {
List<String> testList = ['sfs','sdfsdf', 'sfdsf'];
testList.forEach((value) async {
await startTimeout(30000);
print('Writing another word $value');
});
}
Any idea how I might do this?
Use await Future.delayed() to pause for certain duration and a simple old for(...in...){} loop, instead of forEach() to iterate the values.
If forEach() receives async functions, each iteration call will run in a separate asynchronous context which can be reasoned about similarly to parallel code execution. Meanwhile forEach it self will return immediately without waiting until any async function to complete.
How to Async/await in List.forEach() in Dart
Sample:
https://dartpad.dartlang.org/a57a500d4593aebe1bad0ed79376016c
main() async {
List<String> testList = ['sfs','sdfsdf', 'sfdsf'];
for(final value in testList) {
await Future.delayed(Duration(seconds: 1));
print('Writing another word $value');
};
}
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
}
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