webentwicklung-frage-antwort-db.com.de

Wie erzwinge ich die sequentielle Javascript-Ausführung?

Ich habe nur ziemlich komplizierte Antworten gefunden, die Klassen, Ereignisbehandlungsroutinen und Rückrufe beinhalten (was mir wie ein Vorschlag für einen Vorschlaghammer erscheint). Ich denke, Callbacks mögen nützlich sein, aber ich kann sie nicht im einfachsten Kontext anwenden. Siehe dieses Beispiel:

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

Dabei wird die zweite Funktion vor der ersten Funktion abgeschlossen. Was ist der einfachste Weg (oder gibt es einen?), um die zweite Funktion zu zwingen, die Ausführung zu verzögern, bis die erste Funktion abgeschlossen ist?

---Bearbeiten---

Das war also ein Müllbeispiel, aber dank David Hedlund sehe ich mit diesem neuen Beispiel, dass es tatsächlich synchron ist (zusammen mit dem Absturz meines Browsers im Testprozess!):

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

Da meine ACTUAL-Ausgabe mit jQuery und IE war, muss ich eine separate Frage dazu stellen, wenn ich selbst nicht weiter komme!

60
Tom

Nun, setTimeout hält nach seiner Definition den Thread nicht auf. Dies ist wünschenswert, da sonst die gesamte Benutzeroberfläche für die Wartezeit eingefroren würde. Wenn Sie wirklich setTimeout verwenden müssen, sollten Sie Callback-Funktionen verwenden:

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

Wenn Sie nicht mit setTimeout arbeiten, aber nur Funktionen haben, die sehr lange ausgeführt werden und setTimeout verwenden, um das zu simulieren, dann wären Ihre Funktionen tatsächlich und Sie hätten dieses Problem nicht alles. Es sollte jedoch beachtet werden, dass AJAX -Anfragen asynchron sind und genau wie setTimeout den UI-Thread nicht aufhalten, bis er beendet ist. Bei AJAX müssen Sie wie bei setTimeout mit Callbacks arbeiten.

44
David Hedlund

Nach all dieser Zeit bin ich wieder auf diese Fragen zurück, weil ich so lange gebraucht habe, um zu finden, was ich für eine saubere Lösung halte: Die einzige Möglichkeit, eine sequentielle Ausführung von Javascript zu erzwingen, die ich kenne, ist die Verwendung von Versprechen ... Es gibt ausführliche Erklärungen der Versprechen unter: Versprechen/A und Versprechen/A +

Die einzige Bibliothek, die Versprechen implementiert, die ich kenne, ist Jquery. Hier ist also, wie ich die Frage mit Jquery-Versprechen lösen würde:

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

Indem Sie ein Versprechen implementieren und die Funktionen mit .then () verketten, stellen Sie sicher, dass die zweite Funktion erst ausgeführt wird, nachdem die erste ausgeführt wurde Es ist der Befehl d.resolve () in longfunctionfirst (), der das Signal gibt Starten Sie die nächste Funktion.

Technisch gesehen muss shortfactsecond () kein verzögert erstellen und ein Versprechen zurückgeben, aber ich habe mich in Versprechen verliebt und neige dazu, alles mit Versprechen umzusetzen, sorry.

30
Raymond

Ich bin ein alter Hase beim Programmieren und bin vor kurzem zu meiner alten Leidenschaft zurückgekehrt. Ich bemühe mich, in diese objektorientierte, ereignisgesteuerte, helle Welt zu passen, und während ich die Vorteile des nicht sequentiellen Verhaltens von Javascript sehe, gibt es Zeit, wo es wirklich ankommt Einfachheit und Wiederverwendbarkeit ... Ein einfaches Beispiel, an dem ich gearbeitet habe, war, ein Foto (Handy in Javascript, HTML, Phonegap, ...) zu erstellen, die Größe zu ändern und auf eine Website hochzuladen .. Die ideale Reihenfolge ist:

  1. Mach ein Foto
  2. Laden Sie das Foto in ein Bildelement
  3. Bildgröße ändern (mit Pixastic)
  4. Laden Sie es auf eine Website hoch
  5. Informieren Sie den Benutzer über den Erfolg

Dies alles wäre ein sehr einfaches sequentielles Programm, wenn jeder Schritt die Kontrolle an den nächsten zurückgeben würde, wenn er fertig ist, aber in Wirklichkeit:

  1. Da ein Foto asynchron ist, versucht das Programm, es in das img-Element zu laden, bevor es existiert
  2. Laden Sie das Foto asynchron, so dass das Bild in der Größe verändert wird, bevor das Bild vollständig geladen ist
  3. Die Größenänderung erfolgt asynchron, so dass der Upload auf die Website gestartet wird, bevor die Bildgröße vollständig geändert wird
  4. Der Upload auf die Website erfolgt asynchron. Das Programm wird also fortgesetzt, bevor das Foto vollständig hochgeladen wurde.

Und 4 von 5 Schritten beinhalten Callback-Funktionen.

Meine Lösung besteht also darin, jeden Schritt im vorherigen Schritt zu verschachteln und .onload und andere ähnliche Strategien zu verwenden. Es sieht etwa so aus:

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

(Ich hoffe, dass ich nicht zu viele Fehler gemacht habe, die den Code auf den Punkt bringen.

Dies ist meines Erachtens ein perfektes Beispiel, bei dem async nicht gut ist und sync gut ist, da im Gegensatz zur Ui-Ereignisbehandlung jeder Schritt abgeschlossen sein muss, bevor der nächste ausgeführt wird. Der Code ist jedoch eine russische Puppenkonstruktion, die verwirrend und unlesbar ist. Die Wiederverwendbarkeit des Codes ist schwer zu erreichen, da es bei der ganzen Verschachtelung einfach schwierig ist, alle erforderlichen Parameter an die innere Funktion zu übergeben, ohne sie an jeden Container zu übergeben oder böse globale Variablen zu verwenden, und ich hätte das Ergebnis von allen sehr geschätzt Dieser Code würde mir einen Rückgabewert geben, aber der erste Container wird fertig gestellt, bevor der Rückgabewert verfügbar ist.

Um auf die erste Frage von Tom zurückzukommen: Was wäre die intelligente, leicht lesbare, leicht wiederverwendbare Lösung für ein vor 15 Jahren sehr einfaches Programm, das beispielsweise C und ein dummes elektronisches Board verwendet hätte?

Die Anforderung ist in der Tat so einfach, dass ich den Eindruck habe, dass mir ein grundlegendes Verständnis von Javsascript und moderner Programmierung fehlen muss. Sicherlich soll Technologie die Produktivität steigern, oder?.

Danke für Ihre Geduld

Raymond der Dinosaurier ;-)

11
Raymond

Ich habe den Callback-Weg ausprobiert und konnte dies nicht zum Laufen bringen. Sie müssen verstehen, dass die Werte immer noch atomar sind, auch wenn die Ausführung nicht erfolgt. Zum Beispiel:

alert('1'); <--- diese beiden Funktionen werden gleichzeitig ausgeführt

alert('2'); <--- diese beiden Funktionen werden gleichzeitig ausgeführt

aber so zu tun, zwingt uns, die Reihenfolge der Hinrichtung zu kennen:

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

In Javascript gibt es keine Möglichkeit, den Code warten zu lassen. Ich hatte dieses Problem und so habe ich einen synchronen SJAX-Aufruf an den Server gemacht, und der Server führt den Ruhezustand aus oder führt einige Aktivitäten aus, bevor er zurückkehrt.

ZB von Sync AJAX: http://www.hunlock.com/blogs/Snippets:_Synchronous_AJAX

1
rampr

In Ihrem Beispiel wird die erste Funktion tatsächlich abgeschlossen, bevor die zweite Funktion gestartet wird. setTimeout hält die Ausführung der Funktion nicht an, bis das Zeitlimit erreicht ist. Es startet einfach einen Timer im Hintergrund und führt Ihre Alert-Anweisung nach der angegebenen Zeit aus.

Es gibt keine native Methode, um in JavaScript "zu schlafen". Sie könnten eine Schleife schreiben, die die Zeit prüft, aber das wird den Client stark belasten. Sie können auch den Synchronous-Aufruf AJAX ausführen, wie es von emacsian beschrieben wurde, aber dies würde Ihren Server zusätzlich belasten. Sie sollten dies am besten vermeiden, da dies für die meisten Fälle einfach genug sein sollte, wenn Sie die Funktionsweise von setTimeout verstehen.

1
Franz

Ich hatte das gleiche Problem, das ist meine Lösung:

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

1

Fügen Sie Ihren Code in eine Zeichenfolge ein, iterate, eval, setTimeout und rekursion, um mit den restlichen Zeilen fortzufahren. Ohne Zweifel werde ich das verfeinern oder einfach rausschmeißen, wenn es nicht ins Schwarze trifft. Ich beabsichtige, es zu verwenden, um wirklich grundlegende Benutzertests zu simulieren.

Rekursion und setTimeout machen es sequentiell.

Gedanken?

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

AUSGABE

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

Eine andere Möglichkeit, dies zu betrachten, besteht darin, von einer Funktion zu einer anderen zu koppeln. Ein Array von Funktionen, das für alle Ihre aufgerufenen Funktionen global ist, beispielsweise:

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

Richten Sie dann ein Array von Ganzzahlen für die bestimmten 'f' ein, die Sie ausführen möchten, z

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

Rufen Sie dann eine Anfangsfunktion mit 'runorder' als Parameter auf, z. B. . f_start (runorder);

Dann am Ende jeder Funktion einfach den Index auf das nächste 'f' setzen, um das Runorder-Array auszuführen und auszuführen, wobei 'runorder' weiterhin als Parameter übergeben wird, das Array jedoch um eins reduziert ist.

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

Offensichtlich endet dies in einer Funktion, beispielsweise am Index 0, die nicht an eine andere Funktion gekettet ist .. Dies ist vollständig deterministisch und vermeidet 'Timer'.

0
Allan

Wenn Sie nicht auf reines Javascript bestehen, können Sie in Livescript einen sequentiellen Code erstellen, und sieht ziemlich gut aus. Vielleicht möchten Sie einen Blick auf dieses Beispiel

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

Ausgabe: 

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