How to force Sequential Javascript Execution?

2019-01-21 00:37发布

I've only found rather complicated answers involving classes, event handlers and callbacks (which seem to me to be a somewhat sledgehammer approach). I think callbacks may be useful but I cant seem to apply these in the simplest context. See this example:

<html>
  <head>
    <script type="text/javascript">
      function myfunction()  {
        longfunctionfirst();
        shortfunctionsecond();
      }

      function longfunctionfirst() {
        setTimeout('alert("first function finished");',3000);
      }

      function shortfunctionsecond() {
        setTimeout('alert("second function finished");',200);
      }
    </script>
  </head>
  <body>
    <a href="#" onclick="javascript:myfunction();return false;">Call my function</a>
  </body>
</html>

In this, the second function completes before the first function; what is the simplest way (or is there one?) to force the second function to delay execution until the first function is complete?

---Edit---

So that was a rubbish example but thanks to David Hedlund I see with this new example that it is indeed synchronous (along with crashing my browser in the test process!):

<html>
<head>

<script type="text/javascript">
function myfunction() {
    longfunctionfirst();
    shortfunctionsecond();
}

function longfunctionfirst() {
    var j = 10000;
    for (var i=0; i<j; i++) {
        document.body.innerHTML += i;
    }
    alert("first function finished");
}

function shortfunctionsecond() {
    var j = 10;
    for (var i=0; i<j; i++) {
        document.body.innerHTML += i;
    }
    alert("second function finished");
}
</script>

</head>

<body>
  <a href="#" onclick="javascript:myfunction();return false;">Call my function</a>
</body>
</html>

As my ACTUAL issue was with jQuery and IE I will have to post a separate question about that if I can't get anywhere myself!

10条回答
Melony?
2楼-- · 2019-01-21 01:14

Another way to look at this is to daisy chain from one function to another. Have an array of functions that is global to all your called functions, say:

arrf: [ f_final
       ,f
       ,another_f
       ,f_again ],

Then setup an array of integers to the particular 'f''s you want to run, e.g

var runorder = [1,3,2,0];

Then call an initial function with 'runorder' as a parameter, e.g. f_start(runorder);

Then at the end of each function, just pop the index to the next 'f' to execute off the runorder array and execute it, still passing 'runorder' as a parameter but with the array reduced by one.

var nextf = runorder.shift();
arrf[nextf].call(runorder);

Obviously this terminates in a function, say at index 0, that does not chain onto another function. This is completely deterministic, avoiding 'timers'.

查看更多
该账号已被封号
3楼-- · 2019-01-21 01:15

Well, setTimeout, per its definition, will not hold up the thread. This is desirable, because if it did, it'd freeze the entire UI for the time it was waiting. if you really need to use setTimeout, then you should be using callback functions:

function myfunction() {
    longfunctionfirst(shortfunctionsecond);
}

function longfunctionfirst(callback) {
    setTimeout(function() {
        alert('first function finished');
        if(typeof callback == 'function')
            callback();
    }, 3000);
};

function shortfunctionsecond() {
    setTimeout('alert("second function finished");', 200);
};

If you are not using setTimeout, but are just having functions that execute for very long, and were using setTimeout to simulate that, then your functions would actually be synchronous, and you would not have this problem at all. It should be noted, though, that AJAX requests are asynchronous, and will, just as setTimeout, not hold up the UI thread until it has finished. With AJAX, as with setTimeout, you'll have to work with callbacks.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-01-21 01:17

In your example, the first function does actually complete before the second function is started. setTimeout does not hold execution of the function until the timeout is reached, it will simply start a timer in the background and execute your alert statement after the specified time.

There is no native way of doing a "sleep" in JavaScript. You could write a loop that checks for the time, but that will put a lot of strain on the client. You could also do the Synchronous AJAX call, as emacsian described, but that will put extra load on your server. Your best bet is really to avoid this, which should be simple enough for most cases once you understand how setTimeout works.

查看更多
女痞
5楼-- · 2019-01-21 01:18

If you don't insist on using pure Javascript, you can build a sequential code in Livescript and it looks pretty good. You might want to take a look at this example:

# application
do
    i = 3
    console.log td!, "start"
    <- :lo(op) ->
        console.log td!, "hi #{i}"
        i--
        <- wait-for \something
        if i is 0
            return op! # break
        lo(op)
    <- sleep 1500ms
    <- :lo(op) ->
        console.log td!, "hello #{i}"
        i++
        if i is 3
            return op! # break
        <- sleep 1000ms
        lo(op)
    <- sleep 0
    console.log td!, "heyy"

do
    a = 8
    <- :lo(op) ->
        console.log td!, "this runs in parallel!", a
        a--
        go \something
        if a is 0
            return op! # break
        <- sleep 500ms
        lo(op)

Output:

0ms : start
2ms : hi 3
3ms : this runs in parallel! 8
3ms : hi 2
505ms : this runs in parallel! 7
505ms : hi 1
1007ms : this runs in parallel! 6
1508ms : this runs in parallel! 5
2009ms : this runs in parallel! 4
2509ms : hello 0
2509ms : this runs in parallel! 3
3010ms : this runs in parallel! 2
3509ms : hello 1
3510ms : this runs in parallel! 1
4511ms : hello 2
4511ms : heyy
查看更多
时光不老,我们不散
6楼-- · 2019-01-21 01:27

I had the same problem, this is my solution:

var functionsToCall = new Array();

function f1() {
    $.ajax({
        type:"POST",
        url: "/some/url",
        success: function(data) {
            doSomethingWith(data);
            //When done, call the next function..
            callAFunction("parameter");
        }
    });
}

function f2() {
    /*...*/
    callAFunction("parameter2");
}
function f3() {
    /*...*/
    callAFunction("parameter3");
}
function f4() {
    /*...*/
    callAFunction("parameter4");
}
function f5() {
    /*...*/
    callAFunction("parameter5");
}
function f6() {
    /*...*/
    callAFunction("parameter6");
}
function f7() {
    /*...*/
    callAFunction("parameter7");
}
function f8() {
    /*...*/
    callAFunction("parameter8");
}
function f9() {
    /*...*/
    callAFunction("parameter9");
}
    
function callAllFunctionsSy(params) {
	functionsToCall.push(f1);
	functionsToCall.push(f2);
	functionsToCall.push(f3);
	functionsToCall.push(f4);
	functionsToCall.push(f5);
	functionsToCall.push(f6);
	functionsToCall.push(f7);
	functionsToCall.push(f8);
	functionsToCall.push(f9);
	functionsToCall.reverse();
	callAFunction(params);
}

function callAFunction(params) {
	if (functionsToCall.length > 0) {
		var f=functionsToCall.pop();
		f(params);
	}
}

查看更多
兄弟一词,经得起流年.
7楼-- · 2019-01-21 01:27

Put your code in a string, iterate, eval, setTimeout and recursion to continue with the remaining lines. No doubt I'll refine this or just throw it out if it doesn't hit the mark. My intention is to use it to simulate really, really basic user testing.

The recursion and setTimeout make it sequential.

Thoughts?

var line_pos = 0;
var string =`
    console.log('123');
    console.log('line pos is '+ line_pos);
SLEEP
    console.log('waited');
    console.log('line pos is '+ line_pos);
SLEEP
SLEEP
    console.log('Did i finish?');
`;

var lines = string.split("\n");
var r = function(line_pos){
    for (i = p; i < lines.length; i++) { 
        if(lines[i] == 'SLEEP'){
            setTimeout(function(){r(line_pos+1)},1500);
            return;
        }
        eval (lines[line_pos]);
    }
    console.log('COMPLETED READING LINES');
    return;
}
console.log('STARTED READING LINES');
r.call(this,line_pos);

OUTPUT

STARTED READING LINES
123
124
1 p is 0
undefined
waited
p is 5
125
Did i finish?
COMPLETED READING LINES
查看更多
登录 后发表回答