Вопрос

So, I'm creating a bot to play a video game online (completely legally, there's a server for bot competitions, don't worry). The code is in JavaScript, which I'm just picking up now, and I'm having trouble telling the bot to do something, then wait either by time or by condition until doing something else.

So far, I'm simply wrapping everything in gigantic setTimeout commands, but this is extremely inelegant.

There's not too much code that's relevant to post, but essentially I've made up a lot of code that does things like move to a specific location, brake, chase another player, etc. Most work with a recursive setTimeout command - press certain buttons, reset variables, then start the function again if the condition isn't met. If I want to move to a specific location then brake when I finish with that, generally I'd put code likes this:

moveTarget(target);
brake();
[...Execute rest of code...]

However, JavaScript executes both of these commands at the same time, which obviously doesn't work very well. Is there a way to make something more like this (Or the equivalent with a condition instead of a specified time):

moveTarget(target);
wait3Seconds();
brake();
[...Execute rest of code...]

Without needing to do this:

moveTarget(target);
setTimeout(function(){
    brake();
    [...Execute rest of code...]
},3000);

Given how many of these situations I'll have, my code will end up looking like a gigantic mess of nested setTimeouts, which is not very pretty. Is there a better way? I'm already using JQuery, so I'm perfectly willing to use that if necessary.

Это было полезно?

Решение

A little background...

JavaScript does not handle concurrency in the same way as other programming languages you may be used to. Instead, JavaScript has only the one main thread, and instead has a queue for registering then handling events in order as they occur.

For this reason, it is very important to not do things that would block the thread for long periods of time (like a function such as Thread.sleep() would do). That is why you instead should use setTimeout or setInterval for things like this.

You could always create a custom sleep function to write code in the style you are requesting. This might be something like:

function sleep(millis) {
  var startTime = Date.now();
  while (Date.now() < startTime + millis) {}
}

But this would be VERY, VERY BAD. This would freeze the page while this sleep function is running.

Back to your problem...

You basically need to refactor your code to be more "eventful". Rather than having large, linear chunks of code within the same block, you should write your JavaScript code to use callbacks and timeouts.

So the last code block you showed is really the paradigm you should be following with JavaScript:

moveTarget(target);
setTimeout(function(){
  brake();
  [...Execute rest of code...]
},3000);

However, you can certainly clean things up a LOT, if instead of [...Execute rest of code...], you split that apart into other functions and call them from within your setTimeout callback. Of course, keeping your code blocks small and concise is a good rule of thumb in any programming language.


You might also interested in checking out the request animation frame function.

Другие советы

There isn't really a better way to do that. The reason is that any sort of waiting is going to trigger a task that goes into the task queue. Since javascript is single threaded, it has to use these task queues and as a result it will just keep on executing instructions past the setTimeout if instructions are present.

In order to execute code after your delay, it has to be in the timeout callback or it will execute immediately.

You could do this if you created all of your methods in a chain that had a wait command, similar to the jQuery queue system.

$("<div>").queue(function(next){
    // do a
    a();
    next();
}).delay(3*1000).queue(function(next){
    // after 3 seconds do b
    b();
    next();
}).delay(3*1000).queue(function(next){
    // after 3 more seconds do c
    c();
    next();
}).dequeue(); //.dequeue() starts it, only has to be called to make a queue that isn't currently running begin.

so all you would have to do is maintain the same chain and keep adding to it.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top