如何强制顺序 Javascript 执行?

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

<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 我将不得不张贴一个单独的问题,如果我不能得到任何地方自己!

165629 次浏览

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

例如,同步

好吧,按照 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 线程完成之前不会停留。与使用 setTimeout一样,使用 AJAX 也必须使用回调。

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

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

我在编程方面是个老手,最近又重拾旧爱,努力适应这个面向对象、事件驱动的崭新世界。虽然我看到了 Javascript 非顺序行为的优势,但它确实在简单性和可重用性方面遇到了阻碍。 我做过的一个简单的例子就是拍一张照片(用 javascript,HTML,phonegap 编写的手机) ,调整大小,然后上传到网站上。 理想的顺序是:

  1. 拍张照片
  2. 在 img 元素中加载照片
  3. 调整图片大小(使用 Pixastic)
  4. 上传到一个网站
  5. 通知用户成功失败

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

  1. 拍摄一张照片是异步的,因此程序试图在它存在之前将其加载到 img 元素中
  2. 加载照片是异步的,所以调整图片的大小在 img 完全加载之前就开始了
  3. 调整大小是异步的,所以上传到网站开始之前的图片是完全调整大小
  4. 上传到网站是 asyn,所以程序继续之前的照片是完全上传。

另外,5个步骤中的4个涉及回调函数。

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

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

(我希望我没有犯太多的错误,把代码带到它的本质,真正的东西只是太分散注意力)

我相信这是一个完美的例子,异步不好,同步是好的,因为与 Ui 事件处理相反,我们必须在执行下一步之前完成每一步,但是代码是一个俄罗斯娃娃结构,它是混乱和不可读的,代码的可重用性很难实现,因为所有的嵌套,它只是很难带到内部函数所需的所有参数,而不传递给每个容器轮流或使用邪恶的全局变量,我希望所有这些代码的结果会给我一个返回代码,但第一个容器将完成之前,返回代码将可用。

现在回到 Tom 最初的问题,什么是聪明的,容易阅读的,容易重用的解决方案,对于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 ?.

感谢您的耐心等待

恐龙雷蒙德; -)

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

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

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

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

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

过了这么久,我又回到这个问题上来了,因为我花了很长时间才找到我认为是干净的解决办法: 据我所知,强制执行 javascript 顺序执行的唯一方法是使用承诺。 有详尽的解释承诺: 承诺/答应和承诺/A +

我所知道的唯一实现承诺的库是 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>

通过实现一个承诺和链接的功能与。然后()确保第二个函数只在第一个函数执行后才执行 正是 longfunction tionfirst ()中的 d.Resol()命令给出了启动下一个函数的信号。

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

如果您不坚持使用纯 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)

产出:

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

我也有同样的问题,这是我的解决办法:

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

另一种看待这个问题的方式是从一个函数到另一个函数的菊花链。 有一个对所有调用函数都是全局的函数数组,例如:

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

然后设置一个整数数组到你想要运行的特定‘ f’,例如

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

然后调用一个以‘ runorder’作为参数的初始函数,例如。 F _ start (runorder) ;

然后在每个函数结束时,只需将索引弹出到下一个“ f”以执行 runorder 数组并执行它,仍然将“ runorder”作为参数传递,但数组减少了一个。

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

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

将代码放入字符串、 iterate、 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