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?

Rrryyyaaannn
fonte
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();
} 
Mike Robinson
fonte
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/

philwinkle
fonte
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();
}
})
TuanTruong
fonte
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
});
Domysee
fonte
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.

de Raad
fonte
"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
5

você pode fazer assim

$.when(funtion1()).then(function(){
    funtion2();
})
Jay Momaya
fonte
js.do/code/465839 ok, works ,,
Budi Mulyo
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);
}
Russell
fonte
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>

Sahadeb Patro
fonte
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 ...

StashOfCode
fonte