Wie soll ich 3 Funktionen aufrufen, um sie nacheinander auszuführen?


150

Wenn ich diese Funktionen nacheinander aufrufen muss,

$('#art1').animate({'width':'1000px'},1000);        
$('#art2').animate({'width':'1000px'},1000);        
$('#art3').animate({'width':'1000px'},1000);        

Ich weiß, dass ich in jQuery so etwas tun kann:

$('#art1').animate({'width':'1000px'},1000,'linear',function(){
    $('#art2').animate({'width':'1000px'},1000,'linear',function(){
        $('#art3').animate({'width':'1000px'},1000);        
    });        
});        

Nehmen wir jedoch an, dass ich jQuery nicht verwende und Folgendes aufrufen möchte:

some_3secs_function(some_value);        
some_5secs_function(some_value);        
some_8secs_function(some_value);        

Wie soll ich diese Funktionen aufrufen, um sie auszuführen some_3secs_function, und NACH Beendigung dieses Aufrufs, dann ausführen some_5secs_functionund NACH Beendigung dieses Aufrufs, dann aufrufen some_8secs_function?

AKTUALISIEREN:

Das funktioniert immer noch nicht:

(function(callback){
    $('#art1').animate({'width':'1000px'},1000);
    callback();
})((function(callback2){
    $('#art2').animate({'width':'1000px'},1000);
    callback2();
})(function(){
    $('#art3').animate({'width':'1000px'},1000);
}));

Drei Animationen beginnen gleichzeitig

Wo ist mein Fehler?


Meinen Sie damit, dass die Funktionen in genau 3 5 & 8 Sekunden oder nur nacheinander aufgerufen werden?
Trass Vasston

Ich denke, Sie sind sich bei der Ausführung von synchronen und asynchronen Funktionen einfach nicht sicher. Ich habe meine Antwort unten aktualisiert. Ich hoffe es hilft.
Wayne

Antworten:


242

In Javascript gibt es synchrone und asynchrone Funktionen.

Synchrone Funktionen

Die meisten Funktionen in Javascript sind synchron. Wenn Sie mehrere synchrone Funktionen hintereinander aufrufen würden

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

Sie werden in der richtigen Reihenfolge ausgeführt. doSomethingElsewird nicht gestartet, bis doSomethingabgeschlossen ist. doSomethingUsefulThisTimewird wiederum erst nach Abschluss gestartet doSomethingElse.

Asynchrone Funktionen

Die asynchrone Funktion wartet jedoch nicht aufeinander. Schauen wir uns das gleiche Codebeispiel an, das wir oben hatten, diesmal unter der Annahme, dass die Funktionen asynchron sind

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

Die Funktionen werden der Reihe nach initialisiert, aber alle werden ungefähr zur gleichen Zeit ausgeführt. Sie können nicht konsequent vorhersagen, welches zuerst beendet wird: Dasjenige, dessen Ausführung die kürzeste Zeit in Anspruch nimmt, wird zuerst beendet.

Manchmal möchten Sie jedoch, dass asynchrone Funktionen der Reihe nach ausgeführt werden, und manchmal möchten Sie, dass synchrone Funktionen asynchron ausgeführt werden. Glücklicherweise ist dies mit Rückrufen bzw. Zeitüberschreitungen möglich.

Rückrufe

Nehmen wir an , dass wir drei asynchrone Funktionen haben , dass wir , um ausführen wollen, some_3secs_function, some_5secs_function, und some_8secs_function.

Da Funktionen in Javascript als Argumente übergeben werden können, können Sie eine Funktion als Rückruf übergeben, um sie nach Abschluss der Funktion auszuführen.

Wenn wir die Funktionen so erstellen

function some_3secs_function(value, callback){
  //do stuff
  callback();
}

dann kannst du dann in der folgenden Reihenfolge anrufen:

some_3secs_function(some_value, function() {
  some_5secs_function(other_value, function() {
    some_8secs_function(third_value, function() {
      //All three functions have completed, in order.
    });
  });
});

Zeitüberschreitungen

In Javascript können Sie einer Funktion mitteilen, dass sie nach einer bestimmten Zeitüberschreitung (in Millisekunden) ausgeführt werden soll. Dies kann dazu führen, dass sich synchrone Funktionen asynchron verhalten.

Wenn wir drei synchrone Funktionen haben, können wir sie mit der setTimeoutFunktion asynchron ausführen .

setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);

Dies ist jedoch etwas hässlich und verstößt gegen das DRY-Prinzip [Wikipedia] . Wir könnten dies ein wenig bereinigen, indem wir eine Funktion erstellen, die ein Array von Funktionen und eine Zeitüberschreitung akzeptiert.

function executeAsynchronously(functions, timeout) {
  for(var i = 0; i < functions.length; i++) {
    setTimeout(functions[i], timeout);
  }
}

Dies kann so genannt werden:

executeAsynchronously(
    [doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);

Wenn Sie über asynchrone Funktionen verfügen, die Sie synchron ausführen möchten, verwenden Sie Rückrufe. Wenn Sie über synchrone Funktionen verfügen, die Sie asynchron ausführen möchten, verwenden Sie Timeouts.


7
Dadurch werden die Funktionen nicht um 3,5 und 8 Sekunden verzögert, wie im Beispiel vorgeschlagen. Sie werden nur nacheinander ausgeführt.
Trass Vasston

1
@ Peter - Warte, also bin ich verwirrt. Wenn es sich um einfache synchrone Anrufe handelt, deren Abschluss einige Sekunden dauert, warum brauchen wir dann etwas davon?
Wayne

9
@Peter - +1 für die schönste, verschlungenste Methode, die ich je gesehen habe, um drei synchrone Funktionen nacheinander aufzurufen.
Wayne

4
Vielen Dank, dass Sie den Unterschied zwischen asynchronen und synchronisierten js-Funktionen fachmännisch erklärt haben. Das erklärt so viel.
Nelson

2
Dies ist aus folgenden Gründen NICHT korrekt: (1) Die 3 Zeitüberschreitungen werden alle nach 10 Sekunden aufgelöst, sodass alle 3 Leitungen gleichzeitig ausgelöst werden. (2) Bei dieser Methode müssen Sie die Dauer im Voraus kennen und die "Zeitplan" -Funktionen für die Zukunft planen, anstatt darauf zu warten, dass die früheren asynchronen Funktionen in der Kette aufgelöst werden und dies der Auslöser ist. --- Stattdessen möchten Sie eine der folgenden Antworten verwenden, indem Sie entweder Rückrufe, Versprechen oder die asynchrone Bibliothek verwenden.
Zeroasterisk

37

Diese Antwort verwendet promiseseine JavaScript-Funktion des ECMAScript 6Standards. Wenn Ihre Zielplattform dies nicht unterstützt promises, füllen Sie sie mit PromiseJs .

Schauen Sie sich meine Antwort hier an. Warten Sie, bis eine Funktion mit Animationen fertig ist, bis Sie eine andere Funktion ausführen, wenn Sie jQueryAnimationen verwenden möchten .

So würde Ihr Code mit ES6 Promisesund aussehen jQuery animations.

Promise.resolve($('#art1').animate({ 'width': '1000px' }, 1000).promise()).then(function(){
    return Promise.resolve($('#art2').animate({ 'width': '1000px' }, 1000).promise());
}).then(function(){
    return Promise.resolve($('#art3').animate({ 'width': '1000px' }, 1000).promise());
});

Es können auch normale Methoden eingeschlossen werden Promises.

new Promise(function(fulfill, reject){
    //do something for 5 seconds
    fulfill(result);
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 5 seconds
        fulfill(result);
    });
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 8 seconds
        fulfill(result);
    });
}).then(function(result){
    //do something with the result
});

Die thenMethode wird ausgeführt, sobald der PromiseVorgang abgeschlossen ist. Normalerweise wird der Rückgabewert des functionübergebenen an thenals Ergebnis an den nächsten übergeben.

Wenn jedoch a Promisezurückgegeben wird, thenwartet die nächste Funktion bis zur PromiseAusführung und empfängt die Ergebnisse (den Wert, an den übergeben wird fulfill).


Ich weiß, dass dies nützlich ist, fand den Code jedoch schwer zu verstehen, ohne nach einem Beispiel aus der Praxis zu suchen, um ihm einen Kontext zu geben. Ich fand dieses Video auf YouTube: youtube.com/watch?v=y5mltEaQxa0 - und schrieb hier die Quelle aus dem Video oben drive.google.com/file/d/1NrsAYs1oaxXw0kv9hz7a6LjtOEb6x7z-/... Es gibt einige weitere Nuancen wie der Fang fehlt in Dieses Beispiel, auf das hier näher eingegangen wird. (Verwenden Sie eine andere ID in der Zeile getPostById () oder versuchen Sie, den Namen eines Autors so zu ändern, dass er nicht mit einem Beitrag usw.
übereinstimmt.

20

Es hört sich so an, als würden Sie den Unterschied zwischen synchroner und asynchroner Funktionsausführung nicht vollständig einschätzen .

Der Code, den Sie in Ihrem Update angegeben haben, führt sofort jede Ihrer Rückruffunktionen aus, wodurch wiederum sofort eine Animation gestartet wird. Die Animationen werden jedoch asynchron ausgeführt . Es funktioniert so:

  1. Führen Sie einen Schritt in der Animation aus
  2. Rufen Sie setTimeoutmit einer Funktion auf, die den nächsten Animationsschritt und eine Verzögerung enthält
  3. Einige Zeit vergeht
  4. Der Rückruf, der setTimeoutausgeführt wird
  5. Gehen Sie zurück zu Schritt 1

Dies wird fortgesetzt, bis der letzte Schritt in der Animation abgeschlossen ist. In der Zwischenzeit sind Ihre synchronen Funktionen längst abgeschlossen. Mit anderen Worten, Ihr Aufruf der animateFunktion dauert nicht wirklich 3 Sekunden. Der Effekt wird mit Verzögerungen und Rückrufen simuliert.

Was Sie brauchen, ist eine Warteschlange . Intern stellt jQuery die Animationen in die Warteschlange und führt Ihren Rückruf erst aus, wenn die entsprechende Animation abgeschlossen ist. Wenn Ihr Rückruf dann eine andere Animation startet, werden diese nacheinander ausgeführt.

Im einfachsten Fall entspricht dies dem Folgenden:

window.setTimeout(function() {
    alert("!");
    // set another timeout once the first completes
    window.setTimeout(function() {
        alert("!!");
    }, 1000);
}, 3000); // longer, but first

Hier ist eine allgemeine asynchrone Schleifenfunktion. Es ruft die angegebenen Funktionen der Reihe nach auf und wartet jeweils auf die angegebene Anzahl von Sekunden.

function loop() {
    var args = arguments;
    if (args.length <= 0)
        return;
    (function chain(i) {
        if (i >= args.length || typeof args[i] !== 'function')
            return;
        window.setTimeout(function() {
            args[i]();
            chain(i + 1);
        }, 2000);
    })(0);
}    

Verwendung:

loop(
  function() { alert("sam"); }, 
  function() { alert("sue"); });

Sie können dies natürlich ändern, um konfigurierbare Wartezeiten in Anspruch zu nehmen oder die erste Funktion sofort auszuführen oder die Ausführung zu beenden, wenn eine Funktion in der Kette zurückkehrt falseoder zu applyden Funktionen in einem bestimmten Kontext oder was auch immer Sie sonst benötigen.


14

Ich glaube, die asynchrone Bibliothek bietet Ihnen eine sehr elegante Möglichkeit, dies zu tun. Während es etwas schwierig sein kann, mit Versprechungen und Rückrufen zu jonglieren, kann Async saubere Muster liefern, um Ihren Denkprozess zu rationalisieren. Um Funktionen seriell auszuführen, müssten Sie sie in einen asynchronen Wasserfall stellen . Im asynchronen Jargon wird jede Funktion als a bezeichnet task, die einige Argumente und a benötigt callback. Das ist die nächste Funktion in der Sequenz. Die Grundstruktur würde ungefähr so ​​aussehen:

async.waterfall([
  // A list of functions
  function(callback){
      // Function no. 1 in sequence
      callback(null, arg);
  },
  function(arg, callback){
      // Function no. 2 in sequence
      callback(null);
  }
],    
function(err, results){
   // Optional final callback will get results for all prior functions
});

Ich habe gerade versucht, die Struktur hier kurz zu erklären. Lesen Sie den Wasserfallführer durch , um weitere Informationen zu erhalten. Er ist ziemlich gut geschrieben.


1
Das macht JS wirklich ein bisschen erträglicher.
Mike

9

Ihre Funktionen sollten eine Rückruffunktion annehmen, die nach Abschluss aufgerufen wird.

function fone(callback){
...do something...
callback.apply(this,[]);

}

function ftwo(callback){
...do something...
callback.apply(this,[]);
}

dann wäre die Verwendung wie folgt:

fone(function(){
  ftwo(function(){
   ..ftwo done...
  })
});

4
asec=1000; 

setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);

Ich werde hier nicht auf setTimeout eingehen, aber:

  • In diesem Fall habe ich den Code hinzugefügt, der als Zeichenfolge ausgeführt werden soll. Dies ist der einfachste Weg, eine Variable an Ihre setTimeout-ed-Funktion zu übergeben, aber Puristen werden sich beschweren.
  • Sie können auch einen Funktionsnamen ohne Anführungszeichen übergeben, es kann jedoch keine Variable übergeben werden.
  • Ihr Code wartet nicht darauf, dass setTimeout ausgelöst wird.
  • Dieser kann zunächst schwierig zu verstehen sein: Wenn Sie aufgrund des vorherigen Punkts eine Variable von Ihrer aufrufenden Funktion übergeben, ist diese Variable zum Zeitpunkt des Auslösens des Timeouts nicht mehr vorhanden - die aufrufende Funktion wurde ausgeführt und ist es vars weg.
  • Es ist bekannt, dass ich anonyme Funktionen verwende, um all dies zu umgehen, aber es könnte durchaus einen besseren Weg geben,

3

Da Sie es mit Javascript markiert haben, würde ich eine Timer-Steuerung verwenden, da Ihre Funktionsnamen 3, 5 und 8 Sekunden sind. Starten Sie also Ihren Timer, 3 Sekunden in, rufen Sie den ersten an, 5 Sekunden in rufen Sie den zweiten an, 8 Sekunden in rufen Sie den dritten an, und stoppen Sie dann den Timer, wenn er fertig ist.

Normalerweise ist in Javascript das, was Sie haben, korrekt, da die Funktionen nacheinander ausgeführt werden. Da es jedoch so aussieht, als würden Sie versuchen, zeitgesteuerte Animationen zu erstellen, ist ein Timer die beste Wahl.


2

//sample01
(function(_){_[0]()})([
	function(){$('#art1').animate({'width':'10px'},100,this[1].bind(this))},
	function(){$('#art2').animate({'width':'10px'},100,this[2].bind(this))},
	function(){$('#art3').animate({'width':'10px'},100)},
])

//sample02
(function(_){_.next=function(){_[++_.i].apply(_,arguments)},_[_.i=0]()})([
	function(){$('#art1').animate({'width':'10px'},100,this.next)},
	function(){$('#art2').animate({'width':'10px'},100,this.next)},
	function(){$('#art3').animate({'width':'10px'},100)},
]);

//sample03
(function(_){_.next=function(){return _[++_.i].bind(_)},_[_.i=0]()})([
	function(){$('#art1').animate({'width':'10px'},100,this.next())},
	function(){$('#art2').animate({'width':'10px'},100,this.next())},
	function(){$('#art3').animate({'width':'10px'},100)},
]);


Können Sie bitte erklären, was das ist? Was ist an den Unterstrich gebunden? Was macht die zugewiesene Funktion next?
Mtso

1
Ich erkläre Beispiel 2 mit jsfiddle. jsfiddle.net/mzsteyuy/3 Wenn Sie mir eine grobe Erklärung erlauben, ist Beispiel 2 eine kurze Beschreibung des Codes in jsfiddle. Der Unterstrich ist Array, dessen Elemente gegenwertig sind (i) und als nächstes funktionieren und [0] ~ [2] funktionieren.
Yuuya

1

Sie können Versprechen auch folgendermaßen verwenden:

    some_3secs_function(this.some_value).then(function(){
       some_5secs_function(this.some_other_value).then(function(){
          some_8secs_function(this.some_other_other_value);
       });
    });

Sie müssten some_valueglobal machen, um von innerhalb der .then darauf zugreifen zu können

Alternativ können Sie von der äußeren Funktion den Wert zurückgeben, den die innere Funktion verwenden würde, wie folgt:

    one(some_value).then(function(return_of_one){
       two(return_of_one).then(function(return_of_two){
          three(return_of_two);
       });
    });

0

Ich verwende eine 'waitUntil'-Funktion, die auf dem setTimeout von Javascript basiert

/*
    funcCond : function to call to check whether a condition is true
    readyAction : function to call when the condition was true
    checkInterval : interval to poll <optional>
    timeout : timeout until the setTimeout should stop polling (not 100% accurate. It was accurate enough for my code, but if you need exact milliseconds, please refrain from using Date <optional>
    timeoutfunc : function to call on timeout <optional>
*/
function waitUntil(funcCond, readyAction, checkInterval, timeout, timeoutfunc) {
    if (checkInterval == null) {
        checkInterval = 100; // checkinterval of 100ms by default
    }
    var start = +new Date(); // use the + to convert it to a number immediatly
    if (timeout == null) {
        timeout = Number.POSITIVE_INFINITY; // no timeout by default
    }
    var checkFunc = function() {
        var end = +new Date(); // rough timeout estimations by default

        if (end-start > timeout) {
            if (timeoutfunc){ // if timeout function was defined
                timeoutfunc(); // call timeout function
            }
        } else {
            if(funcCond()) { // if condition was met
                readyAction(); // perform ready action function
            } else {
                setTimeout(checkFunc, checkInterval); // else re-iterate
            }
        }
    };
    checkFunc(); // start check function initially
};

Dies würde perfekt funktionieren, wenn Ihre Funktionen eine bestimmte Bedingung auf true setzen, die Sie abfragen könnten. Außerdem gibt es Timeouts, die Ihnen Alternativen für den Fall bieten, dass Ihre Funktion etwas nicht erledigt hat (auch innerhalb des Zeitbereichs. Denken Sie an Benutzer-Feedback!).

z.B

doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);

Anmerkungen

Das Datum führt zu groben Zeitüberschreitungsschätzungen. Wechseln Sie für eine höhere Genauigkeit zu Funktionen wie console.time (). Beachten Sie, dass Date eine bessere Unterstützung für Browser und Legacy bietet. Wenn Sie keine genauen Millisekundenmessungen benötigen; Machen Sie sich keine Sorgen oder wickeln Sie es alternativ ein und bieten Sie console.time () an, wenn der Browser dies unterstützt


0

Wenn Methode 1 nach Methode 2, 3, 4 ausgeführt werden muss. Das folgende Codefragment kann die Lösung hierfür sein, indem das Objekt "Zurückgestellt" in JavaScript verwendet wird.

function method1(){
  var dfd = new $.Deferred();
     setTimeout(function(){
     console.log("Inside Method - 1"); 
     method2(dfd);	 
    }, 5000);
  return dfd.promise();
}

function method2(dfd){
  setTimeout(function(){
   console.log("Inside Method - 2"); 
   method3(dfd);	
  }, 3000);
}

function method3(dfd){
  setTimeout(function(){
   console.log("Inside Method - 3"); 	
   dfd.resolve();
  }, 3000);
}

function method4(){   
   console.log("Inside Method - 4"); 	
}

var call = method1();

$.when(call).then(function(cb){
  method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.