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?

mrfr
  • 1,724
  • 2
  • 23
  • 44
  • Append paragraphs to `#output_text` instead of overwriting its value? – Bergi Mar 15 '16 at 18:11
  • Hm, would you mind elaborating. Im quite new to HTLM/CSS/Javascript. – mrfr Mar 15 '16 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](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call), its an example using ajax but this is still a way to deal with any asynchronous functions like `setTimeout` – nem035 Mar 15 '16 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? – mrfr Mar 15 '16 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) – nem035 Mar 15 '16 at 19:15
  • Check out [the answer by Jack A.](http://stackoverflow.com/a/36019496/3928341) for more details – nem035 Mar 15 '16 at 19:17

3 Answers3

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.

Jack A.
  • 4,245
  • 1
  • 20
  • 34
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.

Ted A.
  • 2,264
  • 17
  • 22
  • 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! – mrfr Mar 15 '16 at 18:31
  • I don't understand what asynchronous calls have to do with this situation? – Ted A. Mar 15 '16 at 18:32
  • A I dont even know what an `asynchronous call` is. But other people in the commen suggested [this](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call), and I've only, yet, skimmed through it. And it seemed to mention it. – mrfr Mar 15 '16 at 18:43
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.

Will
  • 1,718
  • 3
  • 15
  • 23