Chamar uma função após a conclusão da função anterior


179

Eu tenho o seguinte código JavaScript:

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

Como posso garantir que a function2chamada seja feita apenas após a function1conclusão?


24
está function1executando uma operação assíncrona?
LiraNuna

8
Yads, se a função1 contiver animações, a função2 será executada enquanto as animações da função1 ainda estiverem acontecendo. Como você faria a função2 esperar até que tudo iniciado na função1 estivesse completamente pronto?
trusktr

Alguém pode me explicar por que é necessário fazer algo para garantir que a função2 não seja chamada até que a função1 seja concluída? Não há nenhuma operação assíncrona acontecendo aqui; portanto, a função2 não será executada até que a função1 seja concluída de qualquer maneira, pois esta operação é síncrona.
Aaron

Respostas:


206

Especifique um retorno de chamada anônimo e faça com que function1 aceite:

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


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

11
+1, mas se ele está usando 1.5, ele pode apenas usar o novo padrão Deferreds
philwinkle

Obrigado pelas respostas rápidas). Sim, a função1 está apenas executando uma tarefa assíncrona simples, então acredito que você esteja correto, pois terei que criar um retorno de chamada - ou dê uma olhada nesse negócio de diferido que a philwinkle mencionou. Mais uma vez obrigado, pessoal. Vou dormir um pouco e resolver isso de novo de manhã.
Rrryyyaaannn 15/02

13
Esta resposta não é a solução. Aqui está a prova de que não funciona: jsfiddle.net/trusktr/M2h6e
trusktr

12
@ MikeRobinson Aqui está o problema: e se o ...do stuffcontém coisas que são assíncronas? A função2 ainda não dispara quando esperado. O exemplo no meu comentário acima mostra que, como a foo()função possui código assíncrono, bar()ela não dispara seu conteúdo depois que foo()o conteúdo é executado, mesmo usando $.when().then()para chamá-los um após o outro. Esta resposta é válida apenas se (e somente se) todas as coisas ...do stuffem seu código forem estritamente síncronas.
trusktr

1
@trusktr Nesse caso, você precisa manter passando o primeiro retorno de chamada para baixo para o n º nível de chamada assíncrona, então o fogo callBack()depois de todos n assíncronas chamadas são feitas. Como o OP não mencionou o que está fazendo na função, assumiu-o como uma chamada assíncrona de nível.
GoodSp33d 02/07/2013

96

Se você estiver usando o jQuery 1.5, poderá usar o novo padrão de adiados:

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

Editar: Link atualizado do blog:

Rebecca Murphy teve um ótimo artigo sobre isso aqui: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/


2
Mas isso é um exemplo de trabalho? O que está sendo adiado? Você está executando a função1 e a função2 imediatamente. (Eu não sou o comentarista original.)
user113716 15/02

10
Isso não está funcionando para mim. function1 e function2 são executadas exatamente ao mesmo tempo (conforme observado pelo olho humano). Aqui está o pequeno exemplo: jsfiddle.net/trusktr/M2h6e
trusktr

1
A redação de Rebecca Murphy foi anterior à introdução do Defferds ao jQuery e usa exemplos com base em como o escritor pensa que será implementado. Por favor, visite o site do jQuery para ver como realmente usar este recurso: api.jquery.com/jQuery.Deferred
Spencer Williams

1
Mesmo com o jQuery 1.5 ou algo moderno, você não quer $.when(function1).then(function2);(sem os parênteses que chamam a função)?
Teepeemm

1
Lendo esses comentários alguns anos depois. function1deve retornar uma promessa. Nesse caso, ele precisa ser a função executada real, não a referência. Quando resolveé convocada essa promessa, então function2é executada. Isso é facilmente explicado assumindo que function1há uma chamada ajax. O doneretorno de chamada resolveria a promessa. Espero que esteja claro.
precisa saber é o seguinte

46

Tente o seguinte:

function method1(){
   // some code

}

function method2(){
   // some code
}

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

37

Esta resposta usa promises, um recurso JavaScript do ECMAScript 6padrão. Se sua plataforma de destino não suportar promises, preencha com o PromiseJs .

As promessas são uma maneira nova (e muito melhor) de lidar com operações assíncronas em 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
    });
} 

Isso pode parecer uma sobrecarga significativa para este exemplo simples, mas para código mais complexo é muito melhor do que usar retornos de chamada. Você pode facilmente encadear várias chamadas assíncronas usando várias theninstruções:

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

Você também pode agrupar os adiadores do jQuery facilmente (retornados pelas $.ajaxchamadas):

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

Como observou @charlietfl, o jqXHRobjeto retornado por $.ajax()implementa a Promiseinterface. Portanto, não é realmente necessário envolvê-lo em um Promise, ele pode ser usado diretamente:

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

Promise.resolveembrulho $.ajaxé anti-padrão desde $.ajaxretornos thennable promessa
charlietfl

@charlietfl mas não é real Promise, apenas implementa a interface. Não tenho certeza de como ele se comporta ao passar um real Promisepara seu thenmétodo, ou o que um Promise.allfaria se a jqXHRe a Promisefossem passados ​​para ele. Para isso, preciso fazer mais testes. Mas obrigado pela dica, vou adicioná-la à resposta!
Domysee

Na verdade, no jQuery versão 3+ é compatível com o Promises A +. Independentemente $.ajax.thennão é nova
charlietfl

21

Ou você pode acionar um evento personalizado quando uma função é concluída e vinculá-lo ao documento:

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

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

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

Usando esse método, a função 'b' pode executar APÓS a função 'a', pois o gatilho só existe quando a função a termina de executar.


"A partir do jQuery 1.7, o método .on () é o método preferido para anexar manipuladores de eventos a um documento." api.jquery.com/bind
CodeVirtuoso


3

Isso depende do que a função1 está fazendo.

Se a função1 estiver executando algum javascript sincronizado simples, como atualizar um valor div ou algo assim, a função2 será acionada após a conclusão da função1.

Se a função1 estiver fazendo uma chamada assíncrona, como uma chamada AJAX, você precisará criar um método de "retorno de chamada" (a maioria das APIs do ajax possui um parâmetro de função de retorno de chamada). Em seguida, chame function2 no retorno de chamada. por exemplo:

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

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

2

Se o método 1 precisar ser executado após o método 2, 3, 4. O trecho de código a seguir pode ser a solução para isso usando o objeto Adiado em 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 é alguma função de sincronização que você deseja transformar em uma assíncrona, porque leva algum tempo para concluir, e você não tem controle sobre ela para adicionar um retorno de chamada:

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

A saída será:

Click handled

... e após 2 segundos:

This is function 1
This is function 2

Isso funciona porque chamar window.setTimeout () adicionará uma tarefa ao loop de tarefas JS runtine, que é o que uma chamada assíncrona faz e porque o princípio básico de "execução até a conclusão" do tempo de execução JS garante que onClick () nunca é interrompido antes de terminar.

Observe que isso é tão engraçado quanto pode ser o código difícil de entender ...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.