如何强制顺序Javascript执行?

时间:2009-12-07 10:36:49

标签: javascript asynchronous callback execution synchronous

我只发现了涉及类,事件处理程序和回调的相当复杂的答案(在我看来,这似乎是一种有点大锤的方法)。我认为回调可能有用但我似乎无法在最简单的上下文中应用这些。见这个例子:

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

在此,第二个函数在第一个函数之前完成;什么是最简单的方法(或者有一个?)强制第二个函数延迟执行直到第一个函数完成?

--- ---编辑

所以这是一个垃圾的例子,但多亏了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>

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

9 个答案:

答案 0 :(得分:44)

嗯,根据其定义,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);
};

如果你使用setTimeout,但只是执行了很长时间的函数,并且正在使用setTimeout来模拟它,那么你的函数 实际上是同步的,你根本不会遇到这个问题。但应该注意的是,AJAX请求是异步的,并且就像setTimeout一样,在完成之前不会阻止UI线程。使用AJAX,与setTimeout一样,您将不得不使用回调。

答案 1 :(得分:30)

在这段时间之后我回到了这个问题,因为我花了很长时间才找到我认为干净的解决方案: 强制执行我知道的javascript顺序执行的唯一方法是使用promises。 承诺详尽解释:Promises/APromises/A+

实现我知道的promises的唯一库是jquery所以这里是我如何使用jquery promises来解决这个问题:

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

通过实现一个promise并使用.then()链接函数,确保第二个函数仅在第一个函数执行后执行 它是longfunctionfirst()中的命令d.resolve(),它给出了启动下一个函数的信号。

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

答案 2 :(得分:11)

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

  1. 拍张照片
  2. 将照片加载到img元素
  3. 调整图片大小(使用Pixastic)
  4. 将其上传到网站
  5. 告知用户成功失败
  6. 所有这些都是一个非常简单的顺序程序,如果我们将每个步骤在完成时将控制权返回到下一个程序,但实际上:

    1. 拍摄照片是异步的,因此程序会尝试在img元素存在之前将其加载
    2. 加载照片为异步,以便在img完全加载之前启动调整大小图片
    3. 调整大小是异步的,因此在完全调整图片大小之前上传到网站
    4. 上传到网站是asyn所以程序会在照片完全上传之前继续。
    5. 顺便说一下,5个步骤中的4个涉及回调函数。

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

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

      (我希望我没有犯太多错误,将代码带到它上面,真正的东西太过分散了注意力)

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

      现在回到Tom最初的问题,15年前使用C和哑电子板的简单程序是什么才是智能,易读,易于重用的解决方案?

      这个要求实际上非常简单,我觉得我必须缺少对Javsascript和现代编程的基本理解,当然技术是为了提高生产力吗?

      感谢您的耐心

      恐龙雷蒙德; - )

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

alert('1');&lt; ---这两个函数将同时执行

alert('2');&lt; ---这两个函数将同时执行

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

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

答案 6 :(得分:1)

如果您不坚持使用纯Javascript,则可以在Livescript中构建顺序代码,看起来相当不错。您可能需要查看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)

输出:

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

答案 7 :(得分:1)

我有同样的问题,这是我的解决方案:

=INDEX($X$41:$X$47,MATCH(ROWS($Z$29:Z29),COUNTIF($Z$41:$Z$47,"<="&$Z$41:$Z$47),0))

=INDEX($Y$41:$Y$47,MATCH(ROWS($Z$29:Z29),COUNTIF($Z$41:$Z$47,"<="&$Z$41:$Z$47),0))

=INDEX($Z$41:$Z$47,MATCH(ROWS($Z$29:Z29),COUNTIF($Z$41:$Z$47,"<="&$Z$41:$Z$47),0))

答案 8 :(得分:0)

将代码放在字符串中,迭代,eval,setTimeout和递归以继续其余的行。毫无疑问,如果没有达到标准,我会对此进行改进或者将其抛弃。我的目的是用它来模拟真正的,非常基本的用户测试。

递归和setTimeout使它成为顺序。

思想?

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

输出

STARTED READING LINES
123
124
1 p is 0
undefined
waited
p is 5
125
Did i finish?
COMPLETED READING LINES