如何强制顺序 Javascript 执行?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1859185/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 21:39:18  来源:igfitidea点击:

How to force Sequential Javascript Execution?

javascriptasynchronouscallbackexecutionsynchronous

提问by Tom

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!):

所以这是一个垃圾示例,但感谢 David Hedlund,我在这个新示例中看到它确实是同步的(以及在测试过程中我的浏览器崩溃!):

<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!

由于我的实际问题是 jQuery 和 IE,如果我自己无法到达任何地方,我将不得不发布一个单独的问题!

采纳答案by dxh

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:

好吧,setTimeout根据它的定义,不会阻止线程。这是可取的,因为如果这样做,它会在等待的时候冻结整个 UI。如果你真的需要使用setTimeout,那么你应该使用回调函数:

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 notusing setTimeout, but are just having functions that execute for very long, and were using setTimeoutto simulate that, then your functions wouldactually 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.

如果你使用setTimeout,但只具有执行很长的功能,并使用setTimeout来模拟,那么你的功能实际上是同步的,你就不会在都存在这个问题。不过,应该注意的是,AJAX 请求是异步的,就像 一样setTimeout,在 UI 线程完成之前不会阻止它。使用 AJAX,就像使用 一样setTimeout,您必须使用回调。

回答by Raymond

I am back to this questions after all this time because it took me that long to find what I think is a clean solution : The only way to force a javascript sequential execution that I know of is to use promises. There are exhaustive explications of promises at : Promises/Aand Promises/A+

过了这么久,我又回到了这个问题,因为我花了很长时间才找到我认为是一个干净的解决方案:我所知道的强制 javascript 顺序执行的唯一方法是使用承诺。Promises/APromises/A+处有对 promise 的详尽解释

The only library implementing promises I know is jquery so here is how I would solve the question using jquery promises :

我知道的唯一实现承诺的库是 jquery,所以这里是我将如何使用 jquery 承诺解决问题:

<html>
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script type="text/javascript">
    function myfunction()
    {
        promise = longfunctionfirst().then(shortfunctionsecond);
    }
    function longfunctionfirst()
    {
        d = new $.Deferred();
        setTimeout('alert("first function finished");d.resolve()',3000);
        return d.promise()
    }
    function shortfunctionsecond()
    {
        d = new $.Deferred();
        setTimeout('alert("second function finished");d.resolve()',200);
        return d.promise()
    }
    </script>
</head>
<body>
    <a href="#" onclick="javascript:myfunction();return false;">Call my function</a>
</body>
</html>

By implementing a promise and chaining the functions with .then() you ensure that the second function will be executed only after the first one has executed It is the command d.resolve() in longfunctionfirst() that give the signal to start the next function.

通过实现 promise 并使用 .then() 链接函数,您可以确保仅在第一个函数执行后才会执行第二个函数。 longfunctionfirst() 中的命令 d.resolve() 给出了开始下一个函数的信号功能。

Technically the shortfunctionsecond() does not need to create a deferred and return a promise, but I fell in love with promises and tend to implement everything with promises, sorry.

从技术上讲,shortfunctionsecond() 不需要创建延迟并返回承诺,但我爱上了承诺并倾向于用承诺来实现一切,抱歉。

回答by Raymond

I am an old hand at programming and came back recently to my old passion and am struggling to fit in this Object oriented, event driven bright new world and while i see the advantages of the non sequential behavior of Javascript there are time where it really get in the way of simplicity and reusability. A simple example I have worked on was to take a photo (Mobile phone programmed in javascript, HTML, phonegap, ...), resize it and upload it on a web site. The ideal sequence is :

我是编程的老手,最近又回到了我的旧激情,正在努力适应这个面向对象、事件驱动的明亮新世界,虽然我看到了 Javascript 非顺序行为的优势,但它确实有时间以简单和可重用的方式。我曾经做过的一个简单例子是拍摄一张照片(用 javascript、HTML、phonegap 等编写的手机),调整它的大小并将其上传到网站上。理想的顺序是:

  1. Take a photo
  2. Load the photo in an img element
  3. Resize the picture (Using Pixastic)
  4. Upload it to a web site
  5. Inform the user on success failure
  1. 拍个照
  2. 在 img 元素中加载照片
  3. 调整图片大小(使用 Pixastic)
  4. 将其上传到网站
  5. 通知用户成功失败

All this would be a very simple sequential program if we would have each step returning control to the next one when it is finished, but in reality :

如果我们让每个步骤在完成后将控制权返回到下一个步骤,那么所有这些都将是一个非常简单的顺序程序,但实际上:

  1. Take a photo is async, so the program attempt to load it in the img element before it exist
  2. Load the photo is async so the resize picture start before the img is fully loaded
  3. Resize is async so Upload to the web site start before the Picture is completely resized
  4. Upload to the web site is asyn so the program continue before the photo is completely uploaded.
  1. 拍照是异步的,所以程序会尝试在它存在之前将它加载到 img 元素中
  2. 加载照片是异步的,因此在 img 完全加载之前开始调整图片大小
  3. 调整大小是异步的,因此在图片完全调整大小之前开始上传到网站
  4. 上传到网站是异步的,所以程序会在照片完全上传之前继续。

And btw 4 of the 5 steps involve callback functions.

顺便说一句,这 5 个步骤中有 4 个步骤涉及回调函数。

My solution thus is to nest each step in the previous one and use .onload and other similar stratagems, It look something like this :

因此,我的解决方案是将每个步骤嵌套在前一个步骤中并使用 .onload 和其他类似的策略,它看起来像这样:

takeAPhoto(takeaphotocallback(photo) {
  photo.onload = function () {
    resizePhoto(photo, resizePhotoCallback(photo) {
      uploadPhoto(photo, uploadPhotoCallback(status) {
        informUserOnOutcome();
      });
    }); 
  };
  loadPhoto(photo);
});

(I hope I did not make too many mistakes bringing the code to it's essential the real thing is just too distracting)

(我希望我没有犯太多错误,将代码带到它是必不可少的,真实的东西太分散注意力了)

This is I believe a perfect example where async is no good and sync is good, because contrary to Ui event handling we must have each step finish before the next is executed, but the code is a Russian doll construction, it is confusing and unreadable, the code reusability is difficult to achieve because of all the nesting it is simply difficult to bring to the inner function all the parameters needed without passing them to each container in turn or using evil global variables, and I would have loved that the result of all this code would give me a return code, but the first container will be finished well before the return code will be available.

我相信这是一个完美的例子,其中 async 不好,sync 很好,因为与 Ui 事件处理相反,我们必须在执行下一个步骤之前完成每一步,但代码是俄罗斯娃娃结构,令人困惑且不可读,代码可重用性很难实现,因为所有的嵌套都很难将所有需要的参数带到内部函数中而不依次将它们传递给每个容器或使用邪恶的全局变量,我会喜欢所有的结果这段代码会给我一个返回码,但第一个容器将在返回码可用之前完成。

Now to go back to Tom initial question, what would be the smart, easy to read, easy to reuse solution to what would have been a very simple program 15 years ago using let say C and a dumb electronic board ?

现在回到汤姆最初的问题,对于 15 年前使用比方说 C 和一个愚蠢的电子板的非常简单的程序,智能、易于阅读、易于重用的解决方案是什么?

The requirement is in fact so simple that I have the impression that I must be missing a fundamental understanding of Javsascript and modern programming, Surely technology is meant to fuel productivity right ?.

要求实际上是如此简单,以至于我觉得我必须缺少对 Javsascript 和现代编程的基本了解,当然技术是为了提高生产力,对吧?。

Thanks for your patience

谢谢你的耐心

Raymond the Dinosaur ;-)

恐龙雷蒙德 ;-)

回答by gamadril

I tried the callback way and could not get this to work, what you have to understand is that values are still atomic even though execution is not. For example:

我尝试了回调方式,但无法使其正常工作,您必须了解的是,即使执行不是原子的,值仍然是原子的。例如:

alert('1');<--- these two functions will be executed at the same time

alert('1');<--- 这两个函数会同时执行

alert('2');<--- these two functions will be executed at the same time

alert('2');<--- 这两个函数会同时执行

but doing like this will force us to know the order of execution:

但是这样做会迫使我们知道执行顺序:

loop=2;
total=0;
for(i=0;i<loop;i++) {
           total+=1;
           if(total == loop)
                      alert('2');
           else
                      alert('1');
}

回答by ceremcem

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

如果你不坚持使用纯 Javascript,你可以在Livescript 中构建一个顺序代码,它看起来很不错。你可能想看看这个例子

# 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

回答by Gyergyói Dávid

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);
 }
}

回答by Franz

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.

在您的示例中,第一个函数确实在第二个函数启动之前完成。setTimeout 在达到超时之前不会保持函数的执行,它只会在后台启动一个计时器并在指定时间后执行您的警报语句。

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.

在 JavaScript 中没有进行“睡眠”的原生方式。您可以编写一个循环来检查时间,但这会给客户端带来很大压力。您也可以执行同步 AJAX 调用,如 emacsian 所述,但这会给您的服务器带来额外的负载。最好的办法是避免这种情况,一旦您了解 setTimeout 的工作原理,这对于大多数情况来说应该足够简单。

回答by rampr

In javascript, there is no way, to make the code wait. I've had this problem and the way I did it was do a synchronous SJAX call to the server, and the server actually executes sleep or does some activity before returning and the whole time, the js waits.

在 javascript 中,没有办法让代码等待。我遇到了这个问题,我这样做的方法是对服务器进行同步 SJAX 调用,服务器实际上在返回之前执行 sleep 或执行一些活动,并且整个时间,js 都在等待。

Eg of Sync AJAX: http://www.hunlock.com/blogs/Snippets:_Synchronous_AJAX

例如同步 AJAX:http://www.hunlock.com/blogs/Snippets: _Synchronous_AJAX

回答by Allan

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

然后将整数数组设置为要运行的特定“f”,例如

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

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

然后以'runorder'作为参数调用一个初始函数,例如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.

然后在每个函数的末尾,只需将索引弹出到下一个 'f' 以从 runorder 数组中执行并执行它,仍然将 'runorder' 作为参数传递,但数组减一。

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'.

显然,这在一个函数中终止,比如在索引 0 处,它不会链接到另一个函数上。这是完全确定的,避免了“计时器”。

回答by Jamie Nicholson

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.

将您的代码放在一个字符串中,迭代、评估、setTimeout 和递归以继续剩余的行。毫无疑问,我会改进它,或者如果它没有达到目标就将其扔掉。我的目的是用它来模拟非常非常基本的用户测试。

The recursion and setTimeout make it sequential.

递归和 setTimeout 使其成为连续的。

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