-1

I'm new to JavaScript and I have a ptoblem with a code that looks like this:

function job1() {
    var subText1="";
    var subText2="";
    var text="";
    var vocabulary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz0123456789";

    // Use the delay function to create a full text in 20 * 150 milliseconds
    var i = 0, 
    action = function() {
                var temp = vocabulary.charAt(Math.floor(Math.random() * vocabulary.length));
                text+= temp;
                document.write(text + "<br>");
                i++;
                if (i < 20) {
                            setTimeout(action, 150);
                } else {
                    // The first subText is from position 0 until some random number random between 0 and the text length
                    subText1 = text.slice(0, Math.floor(Math.random() * text.length));
                    // The second subText is from where the first subText ends until the last character form the text input
                    subText2 = text.slice(subText1.length, text.length);

                    // output of the subText1 and the subText2 for the first time
                    document.write("subText1: " + subText1 + "<br>");
                    document.write("subText2: " + subText2 + "<br>");
                    document.write("text: " + text + "<br>");
                }
            };

        setTimeout(action, 0);

        // output of the subText1 and the subText2 once more
        document.write("subText1: " + subText1 + "<br>");
        document.write("subText2: " + subText2 + "<br>");

        // NextJob: job2, job3
        // job dependency
        var nextJob = "job2, job3";
        var prevJob = "null";

        // results
        return { 
        subText1_RT : subText1,
        subText2_RT : subText2
        };
}

my problem is that I need to to get the subText1 and the subText2 from the action = function().... section into this section:

return { 
   subText1_RT : subText1,
   subText2_RT : subText2
};

But the subText1 and the subText2 variables are empty.

Here is a fiddle to the code: http://jsfiddle.net/TalGoz/4tgc372e/

It looks like all the parts of the job1() function are executed before the action = function() part. It is very important to get it working like this as function inside a function, I can’t separate the functions for the purpose of my goal.

I hope someone here can help me see the problem and solve it.

TalG
  • 677
  • 1
  • 9
  • 26
  • 1
    setTimeout and return, do not make sense together. – epascarello Jul 21 '16 at 18:49
  • 1
    @epascarello Why? I don't see any problems using it here. There is nothing wrong with `document.write` if used correctly. The problem is misuse, not don't use. – Spencer Wieczorek Jul 21 '16 at 18:49
  • OP is running setTimeout and document.write, it will replace the page content. Bad use here and 99% of the time it is the wrong thing to use. – epascarello Jul 21 '16 at 18:52
  • @epascarello the document.write is only to see the output. – TalG Jul 21 '16 at 18:53
  • @epascarello I just want to use the `setTimeout` to create my text slowly ,after that I want to do something with the text and then return the results using the 2 variables inside the `job1()`. – TalG Jul 21 '16 at 18:57
  • 1
    You can not return from an asynchronous method. You would need to use a callback to do the final steps after it has completed. – epascarello Jul 21 '16 at 18:57
  • http://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron – epascarello Jul 21 '16 at 18:58
  • @epascarello could you please give me a small example of a callback regarding my code? – TalG Jul 21 '16 at 19:04
  • @TalG, look for a solution to your problem in this question, which is the same problem: http://stackoverflow.com/questions/13455134/javascript-doesnt-seem-to-wait-for-return-values The issue here is that the (job1) function returns before the (action) function even executes, so the subtext values aren't set yet. – Ed Bayiates Jul 21 '16 at 19:43
  • @EdBayiates thanks for the link, I'll check it out. – TalG Jul 21 '16 at 19:51

1 Answers1

1

Edit: The final processing of your text values needs to be in a callback function because of the way JavaScript works -- it is single threaded, meaning only one piece of code runs at a time. Looking at your original code, execution first goes into job1(). It builds action() as a function without executing it and them moves on to the setTimeout(), which sets up action() to run but doesn't run it yet. For a setTimeout() to actually execute, even with time value 0, the current code has to finish executing. Next it moves on to the end code in job1() which is document.Write, job1/job3, and returning the object. Since action() has not yet executed, the subText objects are not yet set.

Once control has left job1(), and finished any current code, the browser will start executing action() from your setTimeout() call. Inside action() there are other setTimeout calls, and those behave similarly -- the current code must finish executing, then there's a wait, and then finally the code specified will execute.

So in summary, you must use a callback function because execution must finish with all current code before any code queued up with setTimeout() will execute. You can't take an asynchronous function like job1() and make it synchronous because none of the async setTimeout() calls can possibly execute while you're inside job1().

In certain other languages, like C++ or Java, you can make code run in parallel, so you can spawn code in a "background thread" and then the current thread can wait for it to finish, giving you the effect you want. You cannot do this with JavaScript. All async functions require a callback. Callback literally means, "Call me back when you're done". In the future, there will be Web Workers which may support multiple pieces of code running at a time: Web Workers

Here is an example using a callback whendone, which is referenced below. It gets called when the values have been set.

function job1(whendone) {
    var subText1="";
    var subText2="";
    var text="";
    var vocabulary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz0123456789";

    // Use the delay function to create a full text in 20 * 150 milliseconds
    var i = 0, 
    action = function() {
            var temp = vocabulary.charAt(Math.floor(Math.random() * vocabulary.length));
            text+= temp;
            document.write(text + "<br>");
            i++;
            if (i < 20) {
                        setTimeout(action, 150);
            } else {
                // The first subText is from position 0 until some random number random between 0 and the text length
                subText1 = text.slice(0, Math.floor(Math.random() * text.length));
                // The second subText is from where the first subText ends until the last character form the text input
                subText2 = text.slice(subText1.length, text.length);

                // output of the subText1 and the subText2 for the first time
                document.write("subText1: " + subText1 + "<br>");
                document.write("subText2: " + subText2 + "<br>");
                document.write("text: " + text + "<br>");

                // Now we're done
                whendone(subText1, subText2);
            }
        };

    setTimeout(action, 0);
}

job1(function(subText1, subText2) {
    // output of the subText1 and the subText2 once more
    document.write("subText1: " + subText1 + "<br>");
    document.write("subText2: " + subText2 + "<br>");

    // NextJob: job2, job3
    // job dependency
    var nextJob = "job2, job3";
    var prevJob = "null";

    // results
    var returned = { 
        subText1_RT : subText1,
        subText2_RT : subText2
    };

    // Your code doing something with 'returned' here
});

The value you want ends up in returned. If you want to do something with the value, add code after the returned = statement.

Ed Bayiates
  • 11,060
  • 4
  • 43
  • 62
  • Thanks for this code!!!, is it possible to capsule the whole thing in one function let’s say `function job(){...}` which will have the output parameters at the end. The thing is with this solution is that at the end I have 2 separate functions that interact with each other. I actually need one `function job(){...}` that I can call and this function can have others functions inside like you have in your example, but at the end only by calling the `job()` function (which capsules all the other) I want to get the results. Again thanks for the code! – TalG Jul 21 '16 at 22:51
  • @TalG, there isn't any way to have a single function. I'll edit my answer to explain more about why. – Ed Bayiates Jul 22 '16 at 16:26
  • 1
    Thanks for the explanation :) – TalG Jul 22 '16 at 17:03