Come devo chiamare 3 funzioni per eseguirle una dopo l'altra?


151

Se devo chiamare queste funzioni una dopo l'altra,

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

So che in jQuery potrei fare qualcosa del tipo:

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

Ma supponiamo che non sto usando jQuery e voglio chiamare:

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

Come dovrei chiamare queste funzioni per eseguire some_3secs_function, e DOPO quella chiamata termina, quindi eseguire some_5secs_functione DOPO quella chiamata termina, quindi chiamare some_8secs_function?

AGGIORNARE:

Questo non funziona ancora:

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

Tre animazioni iniziano contemporaneamente

Dov'è il mio errore?


intendi che le funzioni vengano chiamate esattamente tra 3 5 e 8 secondi o solo una dopo l'altra?
Trass Vasston

Io penso che sei semplicemente incerti circa vs. sincrono esecuzione della funzione asincrona. Ho aggiornato la mia risposta di seguito. Spero che sia d'aiuto.
Wayne,

Risposte:


243

In Javascript, ci sono funzioni sincrone e asincrone .

Funzioni sincrone

La maggior parte delle funzioni in Javascript sono sincrone. Se dovessi chiamare diverse funzioni sincrone di fila

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

eseguiranno in ordine. doSomethingElsenon inizierà fino a quando non sarà doSomethingcompletato. doSomethingUsefulThisTime, a sua volta, non si avvierà fino al doSomethingElsecompletamento.

Funzioni asincrone

La funzione asincrona, tuttavia, non si aspetterà a vicenda. Diamo un'occhiata allo stesso esempio di codice che abbiamo avuto sopra, questa volta supponendo che le funzioni siano asincrone

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

Le funzioni verranno inizializzate in ordine, ma verranno eseguite approssimativamente allo stesso tempo. Non è possibile prevedere in modo coerente quale sarà il primo a finire: quello che per primo impiegherà il minor tempo possibile per terminare.

Ma a volte, si desidera che le funzioni asincrone vengano eseguite in ordine, e talvolta si desidera che le funzioni sincrone vengano eseguite in modo asincrono. Fortunatamente, questo è possibile con callback e timeout, rispettivamente.

callback

Supponiamo che abbiamo tre funzioni asincrone che vogliamo eseguire in ordine, some_3secs_function, some_5secs_function, e some_8secs_function.

Poiché le funzioni possono essere passate come argomenti in Javascript, è possibile passare una funzione come callback da eseguire al termine della funzione.

Se creiamo le funzioni in questo modo

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

quindi puoi chiamare quindi in ordine, in questo modo:

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

timeout

In Javascript, puoi dire a una funzione da eseguire dopo un certo timeout (in millisecondi). In effetti, ciò può far sì che le funzioni sincrone si comportino in modo asincrono.

Se abbiamo tre funzioni sincrone, possiamo eseguirle in modo asincrono usando la setTimeoutfunzione.

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

Questo è, tuttavia, un po 'brutto e viola il principio DRY [wikipedia] . Potremmo risolverlo un po 'creando una funzione che accetta una matrice di funzioni e un timeout.

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

Questo può essere chiamato così:

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

In breve, se si dispone di funzioni asincrone che si desidera eseguire in modo sincrono, utilizzare i callback e se si dispone di funzioni sincrone che si desidera eseguire in modo asincrono, utilizzare i timeout.


7
Ciò non ritarderà le funzioni di 3,5 e 8 secondi, come suggerito dall'esempio, verranno eseguite una dopo l'altra.
Trass Vasston

1
@Peter - Aspetta, quindi sono confuso. Se si tratta di semplici chiamate sincrone che richiedono alcuni secondi per essere completate, allora perché abbiamo bisogno di tutto questo?
Wayne,

9
@Peter - +1 per il metodo più bello e contorto che abbia mai visto per aver chiamato in sequenza tre funzioni sincrone.
Wayne,

4
Grazie per aver spiegato in modo competente la differenza tra le funzioni asincrone e sync js. Questo spiega così tanto.
jnelson,

2
Questo NON è corretto per i seguenti motivi: (1) i 3 timeout si risolvono tutti dopo 10 secondi, quindi tutte e 3 le linee si attivano contemporaneamente. (2) questo metodo richiede di conoscere in anticipo la durata e che le funzioni di "pianificazione" si verifichino in futuro, invece di attendere che le precedenti funzioni asincrone della catena si risolvano e che sia il trigger. --- invece vuoi usare una delle seguenti risposte usando callback, promesse o la libreria asincrona.
zeroasterisk,

37

Questa risposta utilizza promisesuna funzionalità JavaScript dello ECMAScript 6standard. Se la tua piattaforma di destinazione non supporta promises, esegui il polyfill con PromiseJs .

Guarda la mia risposta qui Aspetta che una funzione con animazioni sia finita fino a quando non esegui un'altra funzione se vuoi usare le jQueryanimazioni.

Ecco come sarebbe il tuo codice con ES6 Promisese 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());
});

Si possono anche includere metodi normali 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
});

Il thenmetodo viene eseguito non appena Promiseterminato. Normalmente, il valore di ritorno del functionpassato a thenviene passato al successivo come risultato.

Ma se Promiseviene restituito a, la thenfunzione successiva attende fino al Promisecompletamento dell'esecuzione e ne riceve i risultati (il valore a cui viene passato fulfill).


So che questo è utile, ma ho trovato difficile capire il codice dato senza cercare un esempio del mondo reale per dargli un po 'di contesto. Ho trovato questo video su YouTube: youtube.com/watch?v=y5mltEaQxa0 - e ho scritto la fonte dal video qui drive.google.com/file/d/1NrsAYs1oaxXw0kv9hz7a6LjtOEb6x7z-/… Ci sono altre sfumature come la cattura mancante in questo esempio che questo elabora. (usa un ID diverso nella riga getPostById () o prova a cambiare il nome di un autore in modo che non corrisponda a un post, ecc.)
JGFMK

20

Sembra che tu non stia apprezzando appieno la differenza tra l' esecuzione della funzione sincrona e asincrona .

Il codice fornito nell'aggiornamento esegue immediatamente ciascuna delle funzioni di callback, che a sua volta avvia immediatamente un'animazione. Le animazioni, tuttavia, vengono eseguite in modo asincrono . Funziona così:

  1. Esegui un passaggio nell'animazione
  2. Chiama setTimeoutcon una funzione contenente il passaggio di animazione successivo e un ritardo
  3. Passa del tempo
  4. Il callback dato a setTimeoutviene eseguito
  5. Torna al passaggio 1

Questo continua fino al completamento dell'ultimo passaggio nell'animazione. Nel frattempo, le tue funzioni sincrone sono state completate da tempo. In altre parole, la chiamata alla animatefunzione non richiede realmente 3 secondi. L'effetto è simulato con ritardi e richiamate.

Ciò di cui hai bisogno è una coda . Internamente, jQuery mette in coda le animazioni, eseguendo il callback solo una volta completata l'animazione corrispondente. Se il callback avvia quindi un'altra animazione, l'effetto è che vengono eseguiti in sequenza.

Nel caso più semplice questo equivale al seguente:

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

Ecco una funzione generale di loop asincrono. Chiamerà le funzioni indicate in ordine, aspettando il numero di secondi specificato tra ciascuna.

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

Uso:

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

È ovviamente possibile modificarlo per richiedere tempi di attesa configurabili o per eseguire immediatamente la prima funzione o per interrompere l'esecuzione quando una funzione nella catena ritorna falseo alle applyfunzioni in un contesto specificato o qualsiasi altra cosa di cui si possa avere bisogno.


14

Credo che la biblioteca asincrona ti fornirà un modo molto elegante per farlo. Mentre le promesse e le callback possono diventare un po 'difficili da destreggiarsi, l'async può dare schemi accurati per semplificare il processo di pensiero. Per eseguire funzioni in serie, è necessario inserirle in una cascata asincrona . Nel gergo asincrono, ogni funzione è chiamata a taskche accetta alcuni argomenti e a callback; che è la funzione successiva nella sequenza. La struttura di base sarebbe simile a:

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

Ho appena provato a spiegare brevemente la struttura qui. Leggi la guida alle cascate per ulteriori informazioni, è abbastanza ben scritto.


1
Questo rende JS un po 'più sopportabile.
mike,

9

le tue funzioni dovrebbero avere una funzione di callback, che viene chiamata al termine.

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

}

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

quindi l'utilizzo sarebbe come:

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

Non entrerò in una discussione approfondita di setTimeout qui, ma:

  • in questo caso ho aggiunto il codice da eseguire come stringa. questo è il modo più semplice per passare un var nella tua funzione setTimeout, ma i puristi si lamenteranno.
  • puoi anche passare un nome di funzione senza virgolette, ma nessuna variabile può essere passata.
  • il tuo codice non attende l'attivazione di setTimeout.
  • Questo può essere difficile per prima cosa capovolgerti: a causa del punto precedente, se passi una variabile dalla tua funzione chiamante, quella variabile non esisterà più quando si innesca il timeout - la funzione chiamante sarà eseguita ed è sparito.
  • Sono stato conosciuto per usare funzioni anonime per aggirare tutto ciò, ma potrebbe esserci un modo migliore,

3

Dato che l'hai taggato con JavaScript, andrei con un controllo timer poiché i nomi delle tue funzioni sono 3, 5 e 8 secondi. Quindi avvia il timer, 3 secondi in, chiama il primo, 5 secondi in chiamata il secondo, 8 secondi in chiamata il terzo, quindi al termine, ferma il timer.

Normalmente in Javascript ciò che hai è corretto per le funzioni che si eseguono una dopo l'altra, ma poiché sembra che tu stia provando a fare un'animazione a tempo, un timer sarebbe la soluzione migliore.


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


Puoi per favore spiegare di cosa si tratta? Cosa è legato al carattere di sottolineatura? A cosa serve la funzione assegnata next?
mtso

1
Spiego l'esempio 2 con l'utilizzo di jsfiddle. jsfiddle.net/mzsteyuy/3 Se mi permetti di spiegare in modo approssimativo, l'esempio 2 è una breve via del codice in jsfiddle. il carattere di sottolineatura è Array quali elementi sono contabili (i) e funzionano successivamente e funzionano [0] ~ [2].
yuuya,

1

Puoi anche usare le promesse in questo modo:

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

Dovresti diventare some_valueglobale per accedervi dall'interno di .then

In alternativa, dalla funzione esterna potresti restituire il valore che la funzione interna userebbe, in questo modo:

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

0

Uso una funzione 'waitUntil' basata sul setTimeout di javascript

/*
    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
};

Funzionerebbe perfettamente se le tue funzioni impostassero una certa condizione su true, che potresti sondare. Inoltre viene fornito con timeout, che offre alternative nel caso in cui la funzione non riesca a fare qualcosa (anche entro l'intervallo di tempo. Pensa al feedback degli utenti!)

per esempio

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

Appunti

La data provoca stime approssimative di timeout. Per una maggiore precisione, passa a funzioni come console.time (). Tieni presente che Date offre un maggiore supporto tra browser e legacy. Se non hai bisogno di misurazioni esatte in millisecondi; non preoccuparti, o, in alternativa, avvolgilo e offri console.time () quando il browser lo supporta


0

Se il metodo 1 deve essere eseguito dopo il metodo 2, 3, 4. Il seguente frammento di codice può essere la soluzione per questo utilizzando l'oggetto differito in JavaScript.

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>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.