Llamar a una función después de completar la función anterior


179

Tengo el siguiente código JavaScript:

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

¿Cómo puedo asegurarme de que function2se llame solo después de que se function1haya completado?


24
está function1realizando una operación asincrónica?
LiraNuna

8
Yads, si la función1 contiene animaciones, la función2 se ejecutará mientras las animaciones de la función1 todavía están sucediendo. ¿Cómo haría que la función2 esperara hasta que todo comenzara en la función1 esté completamente terminado?
trusktr

¿Alguien puede explicarme por qué debe hacerse algo para garantizar que la función2 no se invoque hasta que se complete la función1? Aquí no hay una operación asincrónica, por lo que la función2 no se ejecutará hasta que la función1 se complete de todos modos, ya que esta operación es sincrónica.
Aaron

Respuestas:


206

Especifique una devolución de llamada anónima y haga que function1 lo acepte:

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


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

11
+1, pero si está usando 1.5, puede usar el nuevo patrón de diferidos
philwinkle

Gracias por las rápidas respuestas). Sí, function1 solo está realizando una tarea asincrónica simple, así que creo que tienes razón en que tendré que crear una devolución de llamada, o echar un vistazo a este negocio diferido que ha mencionado philwinkle. Gracias de nuevo, muchachos. Voy a dormir un poco y abordaré esto de nuevo por la mañana.
Rrryyyaaannn

13
Esta respuesta no es la solución. Aquí hay pruebas de que no funciona: jsfiddle.net/trusktr/M2h6e
trusktr

12
@MikeRobinson Aquí está el problema: ¿Qué pasa si ...do stuffcontiene cosas que son asíncronas? La función 2 todavía no se activará cuando se espera. El ejemplo en mi comentario anterior muestra que debido a que la foo()función tiene código asincrónico, bar()no dispara su contenido después foo()de que el contenido se haya ejecutado, incluso usando $.when().then()para llamarlos uno tras otro. Esta respuesta solo es válida si (y solo si) todas las cosas ...do stuffen su código son estrictamente sincrónicas.
trusktr

1
@trusktr En ese caso, es necesario mantener pasar la primera llamada de retorno hasta el n º nivel de llamada asincrónica, entonces el fuego callBack()después de todos n asíncronos llamadas se realizan. Dado que OP no ha mencionado lo que está haciendo en la función, lo asumió como una llamada asincrónica de un nivel.
GoodSp33d

96

Si está utilizando jQuery 1.5, puede utilizar el nuevo patrón de diferidos:

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

Editar: Enlace actualizado del blog:

Rebecca Murphy tuvo una gran reseña sobre esto aquí: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/


2
¿Pero es ese un ejemplo de trabajo? ¿Qué se está aplazando? Estás ejecutando function1 y function2 inmediatamente. (No soy el comentarista original)
User113716

10
Eso no está funcionando en absoluto para mí. Las funciones function1 y function2 se ejecutan exactamente al mismo tiempo (como puede observar el ojo humano). Aquí está el pequeño ejemplo: jsfiddle.net/trusktr/M2h6e
trusktr

1
La redacción de Rebecca Murphy fue antes de que Defferds se introdujera en jQuery y utiliza ejemplos basados ​​en cómo el escritor cree que se implementará. Visite el sitio de jQuery para ver cómo usar realmente esta función: api.jquery.com/jQuery.Deferred
Spencer Williams

1
Incluso con jQuery 1.5 o algo moderno, ¿no quieres $.when(function1).then(function2);(sin los paréntesis que llaman a la función)?
Teepeemm

1
Leyendo estos comentarios unos años tarde. function1Debería devolver una promesa. En ese caso, debe ser la función ejecutada real, no la referencia. Cuando resolvese llama a esa promesa, function2se ejecuta. Esto se explica fácilmente asumiendo que function1tiene una llamada ajax. La donedevolución de llamada resolvería la promesa. Espero que sea claro.
philwinkle

46

Prueba esto :

function method1(){
   // some code

}

function method2(){
   // some code
}

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

37

Esta respuesta utiliza promises, una característica de JavaScript del ECMAScript 6estándar. Si su plataforma de destino no es compatible promises, complétela con PromiseJs .

Las promesas son una forma nueva (y mucho mejor) de manejar operaciones asincrónicas en 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
    });
} 

Esto puede parecer una sobrecarga significativa para este ejemplo simple, pero para un código más complejo es mucho mejor que usar devoluciones de llamada. Puede encadenar fácilmente múltiples llamadas asincrónicas utilizando múltiples thendeclaraciones:

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

También puede ajustar los aplazamientos de jQuery fácilmente (que se devuelven de las $.ajaxllamadas):

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

Como señaló @charlietfl, el jqXHRobjeto devuelto por $.ajax()implementa la Promiseinterfaz. Por lo tanto, no es realmente necesario envolverlo en un Promise, se puede usar directamente:

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

Promise.resolveel envoltorio $.ajaxes antipatrón ya que $.ajaxdevuelve la promesa posible
charlietfl

@charlietfl pero no es real Promise, solo implementa la interfaz. No estoy seguro de cómo se comporta cuando pasa un real Promisea su thenmétodo, o qué Promise.allharía si a jqXHRy a Promisese le pasaran. Para eso necesito hacer más pruebas. Pero gracias por la pista, ¡lo agregaré a la respuesta!
Domysee

En realidad, en jQuery versión 3+ es compatible con Promesas A +. Irregardless $.ajax.thenno es nuevo
charlietfl

21

O puede activar un evento personalizado cuando se completa una función, luego vincularlo 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);

Con este método, la función 'b' solo puede ejecutarse DESPUÉS de la función 'a', ya que el disparador solo existe cuando la función a termina de ejecutarse.


"A partir de jQuery 1.7, el método .on () es el método preferido para adjuntar controladores de eventos a un documento". api.jquery.com/bind
CodeVirtuoso


3

Esto depende de qué función esté haciendo.

Si function1 está haciendo un javascript sincrónico simple, como actualizar un valor div o algo así, function2 se activará después de que se haya completado function1.

Si function1 está haciendo una llamada asincrónica, como una llamada AJAX, deberá crear un método de "devolución de llamada" (la mayoría de las API de ajax tienen un parámetro de función de devolución de llamada). Luego llame a function2 en la devolución de llamada. p.ej:

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

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

2

Si el método 1 tiene que ejecutarse después de los métodos 2, 3, 4. El siguiente fragmento de código puede ser la solución para esto usando el objeto diferido en 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

Si function1 es alguna función de sincronización que desea convertir en una asíncrona porque lleva un tiempo completarla, y no tiene control sobre ella para agregar una devolución de llamada:

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

El resultado será:

Click handled

... y después de 2 segundos:

This is function 1
This is function 2

Esto funciona porque llamar a window.setTimeout () agregará una tarea al bucle de tareas de rutina JS, que es lo que hace una llamada asíncrona, y porque el principio básico de "ejecución hasta la finalización" del tiempo de ejecución de JS asegura que onClick () nunca se interrumpe antes de que termine.

Tenga en cuenta que esto es tan divertido como puede ser difícil de entender el código ...

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.