Chiamare una funzione al termine della funzione precedente


179

Ho il seguente codice JavaScript:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable);
        function2(someOtherVariable);
    }
    else {
        doThis(someVariable);
    }
});

Come posso assicurarmi che function2venga chiamato solo dopo che function1è stato completato?


24
sta function1eseguendo un'operazione asincrona?
LiraNuna,

8
Sì, se la funzione1 contiene animazioni, la funzione2 verrà eseguita mentre le animazioni della funzione1 continuano a verificarsi. Come faresti che function2 aspetti fino a quando tutto è iniziato in function1 è completamente fatto?
trusktr,

Qualcuno può spiegarmi perché è necessario fare qualcosa per garantire che la funzione2 non venga invocata fino al completamento della funzione1? Non è in corso alcuna operazione asincrona, pertanto la funzione2 non verrà eseguita fino a quando la funzione 1 non sarà comunque completata poiché l'operazione è sincrona.
Aaron,

Risposte:


206

Specificare un callback anonimo e far accettare a function1:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable, function() {
          function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
  ...do stuff
  callback();
} 

11
+1 ma se sta usando 1.5 può semplicemente usare il nuovo modello Deferreds
Philwinkle,

Grazie per la risposta rapida (s). Sì, function1 sta semplicemente eseguendo una semplice attività asincrona, quindi credo che tu abbia ragione nel dover creare un callback o dare un'occhiata a questo business differito che Philwinkle ha menzionato. Grazie ancora ragazzi. Dormirò un po 'e lo affronterò di nuovo al mattino.
Rrryyyaaannn,

13
Questa risposta non è la soluzione. Ecco la prova che non funziona: jsfiddle.net/trusktr/M2h6e
trusktr

12
@MikeRobinson Ecco il problema: cosa succede se ...do stuffcontiene elementi asincroni? Function2 non funzionerà ancora quando previsto. L'esempio nel mio commento sopra mostra che, poiché la foo()funzione ha un codice asincrono, bar()non attiva il contenuto dopo che foo()il contenuto è stato eseguito, anche usando $.when().then()per chiamarli uno dopo l'altro. Questa risposta è valida solo se (e solo se) tutte le cose contenute ...do stuffnel tuo codice sono strettamente sincrone.
trusktr,

1
@trusktr In questo caso è necessario mantenere passando il primo richiamata fino al n ° livello di chiamata asincrona, quindi il fuoco callBack()dopo che tutti i n asincroni le chiamate sono fatte. Poiché OP non ha menzionato ciò che sta facendo nella funzione, l'ha assunto come una chiamata asincrona di livello.
GoodSp33d

96

Se stai usando jQuery 1.5 puoi usare il nuovo modello Deferreds:

$('a.button').click(function(){
    if(condition == 'true'){
        $.when(function1()).then(function2());
    }
    else {
        doThis(someVariable);
    }
});

Modifica: collegamento blog aggiornato:

Rebecca Murphy ha scritto molto su questo qui: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/


2
Ma è un esempio funzionante? Cosa viene rinviato? Stai eseguendo immediatamente la funzione1 e la funzione2. (Non sono il commentatore originale.)
user113716,

10
Non funziona affatto per me. La funzione 1 e la funzione 2 vengono entrambe eseguite esattamente nello stesso momento (come osservabile dall'occhio umano). Ecco il piccolo esempio: jsfiddle.net/trusktr/M2h6e
trusktr

1
La redazione di Rebecca Murphy avveniva prima che Defferds fosse presentato a jQuery e utilizza esempi basati su come lo scrittore pensa che sarà implementato. Visitare il sito jQuery per vedere come utilizzare effettivamente questa funzione: api.jquery.com/jQuery.Deferred
Spencer Williams

1
Anche con jQuery 1.5 o qualcosa di moderno, non vuoi $.when(function1).then(function2);(senza le parentesi che chiamano la funzione)?
Teepeemm,

1
Leggendo questi commenti con qualche anno di ritardo. function1dovrebbe restituire una promessa. In tal caso, deve essere la funzione effettivamente eseguita, non il riferimento. Quando resolveviene chiamato, quella promessa function2viene eseguita. Ciò è facilmente spiegabile supponendo che function1abbia una chiamata Ajax. Il donecallback avrebbe quindi risolto la promessa. Spero sia chiaro.
Filwinkle

46

Prova questo :

function method1(){
   // some code

}

function method2(){
   // some code
}

$.ajax({
   url:method1(),
   success:function(){
   method2();
}
})

37

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

Le promesse sono un nuovo (e molto migliore) modo per gestire le operazioni asincrone in JavaScript:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable).then(function() {
            //this function is executed after function1
            function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
    return new Promise(function (fulfill, reject){
        //do stuff
        fulfill(result); //if the action succeeded
        reject(error); //if the action did not succeed
    });
} 

Questo può sembrare un notevole sovraccarico per questo semplice esempio, ma per un codice più complesso è molto meglio dell'uso dei callback. Puoi facilmente concatenare più chiamate asincrone usando più thenistruzioni:

function1(someVariable).then(function() {
    function2(someOtherVariable);
}).then(function() {
    function3();
});

Puoi anche avvolgere facilmente i rinvii jQuery (che vengono restituiti dalle $.ajaxchiamate):

Promise.resolve($.ajax(...params...)).then(function(result) {
    //whatever you want to do after the request
});

Come notato da @charlietfl, l' jqXHRoggetto restituito da $.ajax()implementa l' Promiseinterfaccia. Quindi non è effettivamente necessario avvolgerlo in un Promise, può essere utilizzato direttamente:

$.ajax(...params...).then(function(result) {
    //whatever you want to do after the request
});

Promise.resolveil wrapping $.ajaxè anti-pattern poiché $.ajaxrestituisce quindi una promessa
charlietfl

@charlietfl ma non è un reale Promise, implementa semplicemente l'interfaccia. Non sono sicuro di come si comporti quando passa un reale Promiseal suo thenmetodo o cosa Promise.allfarebbe se a jqXHRe a gli fossero Promisepassati. Per questo ho bisogno di fare ulteriori test. Ma grazie per il suggerimento, lo aggiungerò alla risposta!
Domysee,

In realtà in jQuery versione 3+ è conforme a Promises A +. Irregardless $.ajax.thennon è nuovo
charlietfl

21

Oppure puoi attivare un evento personalizzato al completamento di una funzione, quindi associarlo al documento:

function a() {
    // first function code here
    $(document).trigger('function_a_complete');
}

function b() {
    // second function code here
}

$(document).bind('function_a_complete', b);

Utilizzando questo metodo, la funzione 'b' può eseguire solo la funzione DOPO 'a', poiché il trigger esiste solo al termine dell'esecuzione della funzione a.


"A partire da jQuery 1.7, il metodo .on () è il metodo preferito per associare i gestori di eventi a un documento." api.jquery.com/bind
CodeVirtuoso


3

Questo dipende da cosa sta facendo function1.

Se function1 sta eseguendo un semplice javascript sincrono, come l'aggiornamento di un valore div o qualcosa del genere, allora function2 si attiverà al completamento di function1.

Se function1 sta effettuando una chiamata asincrona, come una chiamata AJAX, sarà necessario creare un metodo di "callback" (la maggior parte delle API di ajax ha un parametro di funzione di callback). Quindi chiamare function2 nel callback. per esempio:

function1()
{
  new AjaxCall(ajaxOptions, MyCallback);
}

function MyCallback(result)
{
  function2(result);
}

2

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>


0

Se function1 è una funzione di sincronizzazione che si desidera trasformare in una asincrona perché richiede del tempo per il completamento e non si ha alcun controllo su di essa per aggiungere un callback:

function function1 (someVariable) {
    var date = Date.now ();
    while (Date.now () - date < 2000);      // function1 takes some time to complete
    console.log (someVariable);
}
function function2 (someVariable) {
    console.log (someVariable);
}
function onClick () {
    window.setTimeout (() => { function1 ("This is function1"); }, 0);
    window.setTimeout (() => { function2 ("This is function2"); }, 0);
    console.log ("Click handled");  // To show that the function will return before both functions are executed
}
onClick ();

L'output sarà:

Click handled

... e dopo 2 secondi:

This is function 1
This is function 2

Questo funziona perché chiamando window.setTimeout () aggiungerà un'attività al ciclo delle attività runtine JS, che è ciò che fa una chiamata asincrona, e perché il principio di base del "run-to-completamento" del runtime JS assicura che onClick () non viene mai interrotto prima della fine.

Si noti che questo è tanto divertente quanto potrebbe essere difficile capire il codice ...

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.