0

So I have this function

function main()
{
    //get the start text
    var start_text = document.getElementById("start_text").value;

    //get target text
    var target_text = document.getElementById("target_text").value;

    // get characters to use for modifying string
    var characters = document.getElementById("characters").value;

    // get the mutation rate
    var mutation_rate = document.getElementById("mutation_rate").value;

    // get the amount of offspring for each generation
    var amount_offspring =     document.getElementById("amount_offspring").value;

    // send all the input into generation, to generate a generation of offspring.
    // this function will return the highest scoring offspring of each generation
    best_offspring = generation(start_text, characters, amount_offspring, mutation_rate, target_text, generations);

    // keep looping untill the target_text is met
    while(score_met == false)
    {
        //send the highest scoring offspring again
        best_offspring = generation(best_offspring, characters, amount_offspring, mutation_rate, target_text);
        // output the best offspring
        document.getElementById("output_text").value = best_offspring;
        // output the amount of generations
        document.getElementById("generations").value = generations;
    }
}

Everything works, except that it outputs the finnished string when the while loop is done.

What I want is for it to output each generations best_offspring so that you can see the string "evolve" in real time. I tried to use the function setTimout() like such:

    while(score_met == false)
{
    //send the highest scoring offspring again
    best_offspring = setTimeout(generation(), 1000, best_offspring, characters, amount_offspring, mutation_rate, target_text);
    document.getElementById("output_text").value = best_offspring;
    document.getElementById("generations").value = generations;
}

but didnt get it to work. Any ideas?

6
  • Append paragraphs to #output_text instead of overwriting its value? Commented Mar 15, 2016 at 18:11
  • Hm, would you mind elaborating. Im quite new to HTLM/CSS/Javascript. Commented Mar 15, 2016 at 18:12
  • setTimeout returns its id, and calls the function you pass as the first parameter after the number of milliseconds you pass as the second parameter. Right now you are calling your function generation() before you pass it in (notice the ()). This should help, its an example using ajax but this is still a way to deal with any asynchronous functions like setTimeout Commented Mar 15, 2016 at 18:15
  • @nem Alright, gonna give that a read. From what Ive gatherd yet is that I should probobly use ajax to send asynchronous calls to the function. But as far as I understod is that with the help of ajax I can sendasynchronous calls to the server. But since all this is client side, will it still work? Commented Mar 15, 2016 at 18:24
  • Async behavior is present everywhere in javascript. Ajax is just a good example. You don't need to use ajax at all, its just a common example and a good one for you because setTimeout behaves similarly in the sense that it takes in a function (callback) that is called on some event (after certain amount of milliseconds) Commented Mar 15, 2016 at 19:15

3 Answers 3

2

The first thing you need to understand about JavaScript is that the changes you make to the HTML DOM in JavaScript are not shown to the user until after the JavaScript completes (at least this is the case for all major browser with which I am familiar). This means that when you update the DOM in a loop like you are doing, you will only see the last value.

You were close to the right solution with using setTimeout, but your code for that is not correct, and it is still being done in a loop. What you need to do is a series of steps like this:

  • Call generation and update the DOM
  • Allow the display to update
  • Call generation and update the DOM
  • Allow the display to update
  • and so on...

So rather than calling generation in a loop, you need to call it from within an event handler that completes and allows the display to update before being called again. The code that is executed by setTimeout is exactly the kind of event handler that will work for this. However, in your case, the setInterval function is really a better choice.

The setInterval function takes a function and a delay (in milliseconds) and repeatedly calls that function until it is cancelled.

One of the tricky things with using either of these functions in your case is that you need to maintain state between calls (for example, the score_met and best_offspring variables). For now I'm going to suggest that you use global variables because it is the simplest, but a better solution is to use a closure.

So, where you originally had:

while(score_met == false)
{
    best_offspring = generation(best_offspring, characters, amount_offspring, mutation_rate, target_text);
    document.getElementById("output_text").value = best_offspring;
    document.getElementById("generations").value = generations;
}

You would replace this with:

var interval = setInterval(function () {
    best_offspring = generation(best_offspring, characters, amount_offspring, mutation_rate, target_text);
    document.getElementById("output_text").value = best_offspring;
    document.getElementById("generations").value = generations;
    if (score_met) {
        clearInterval(interval);
    }
}, 1000); // runs every second

This will run your generation function once a second and update the DOM each time it runs. It will stop running it once the score_met variable is set to true.

Sign up to request clarification or add additional context in comments.

2 Comments

@FelixRosen, you should check out this answer
Worked like a charm!
0

Right now at each iteration of your while loop you are setting the value of output text to be the current best_offspring, using the = operator.

It sounds like what you want to do is append the current best_offspring instead. This can be done with the += operator.

Try replacing

// output the best offspring
document.getElementById("output_text").value = best_offspring;

with:

// output the best offspring
document.getElementById("output_text").value += best_offspring;

And see if that does what you want.

3 Comments

Yeah, I want it to overwrite the previous value at each iteration. I want to see the string "grow" from the start_text into the target_text but now it simply outputs the completed string. Previous comments have told me about asynchronous calls though!
I don't understand what asynchronous calls have to do with this situation?
A I dont even know what an asynchronous call is. But other people in the commen suggested this, and I've only, yet, skimmed through it. And it seemed to mention it.
0

Try this:

// this will set the Interval to run in the event queue, but gives the UI a chance to get updated
var loopId = setInterval(function() {
    //send the highest scoring offspring again
    best_offspring = generation(best_offspring, characters, amount_offspring, mutation_rate, target_text);
    document.getElementById("output_text").value = best_offspring;
    document.getElementById("generations").value = generations;
    if (score_met == false) {
        // this clears the loop when the condition is net
        clearInterval(loopId);
    }
}, 0);  // zero should be enough to update the display

Here's an example I did on my page:

var loopId = setInterval(function() {
    var x = Math.floor(Math.random() * 1000) + 1;
    $('#someInputId').val(x);
    if (x === 1000) {
        clearInterval(loopId);
    }
}, 0);

It display a bunch of random numbers in the input box until that value is 1000.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.