Para respeitar a ordem de f1, f2 e f3, a primeira coisa que precisa fazer é mudar o tempo de espera para cada um executar.
Executa na ordem que você determinou que deve esperar para começar a executar. Isso nada tem a ver com callback. Sem colocar todos com o mesmo tempo fica impossível determinar ordem, porque o tempo é que determinará.
Quando quer testar uma coisa, teste só aquela coisa, nunca coloque outro componente junto porque aí não sabe qual está fazendo dar aquele resultado. Ou seja, se quer saber de callback, tire evento controlado por relógio, isso não faz sentido para o problema.
Se colocar tudo 1000ms ainda executará em ordem inversa, porque é assim que funciona a chamada. Isso nada tem a ver com callback, tem a ver com precedência de execução normal da linguagem. O primeiro a executar é sempre o que está dentro de parênteses. Portanto o f3()
é o que está mais dentro é o primeiro a executar, depois o f2()
, e finalmente o f1()
que não está dentro da parênteses.
Portanto, se quer executar na ordem numérica, tem que inverter a chamada. Se quer que o f1()
seja o primeiro a ser executado, ele tem que estar dentro dos parênteses mais internos.
f3(f2(f1()));
Também pode fazer:
f1();
f2();
f3();
Quando você faz:
f1(f2);
Está chamando só a f1()
aí. Aqui começa entrar callback. Você não está chamando f2
. Eu sei disso porque não tem os parênteses, que é a forma de indicar para o compilador que deseja a execução daquele identificar que eve ser uma função (o objeto nessa variável é uma function
). Sem os parênteses é como passar um objeto de uma variável, não é uma chamada para executar. Então f1()
recebe f2
no parâmetro. E ela será executada lá dentro.
Note que o parâmetro chama callback
, então o objeto que estava em f2
agora está em callback
pela passagem durante a chamada da função. Ela é executada depois de imprimir f1
, por isso que você vê depois, se inverter a ordem e colocar o if
e o bloco dele antes do console.log()
, aparecerá f2 -> f1
. Isso é fluxo de execução do mais básico.
Se você passar o objeto que é uma função, não tem como passar um parâmetro (tem técnicas se for necessário, mas não da mesma forma, e é mais avançado, e quase sempre é um erro). Então teve que fazer duas chamadas de função, por precedência, que é a mesma coisa que fazer:
f2(f3);
f1();
Ou seja, chamou f2()
passando o objeto de f3
para executar lá dentro, e depois chamou f1()
.
E como em f1()
não está passando nada e antes de executar o código testa se há um objeto válido, e neste caso não tem, ele não chama nada.
Já em:
f1(function(){
f2(function(){
f3(function(){
console.log('Terminei') //Meramente para mostrar que chegou até o F3
})
})
})
Todos os parêmtros são callbacks. Está bem explícito. O código está chamando f1()
passando como argumeto um callback que chama f2()
, que por sua vez passa como argumeto um callback que chama f3()
, então dentro de f1()
, ele chama f2()
, que por sua vez chama f3()
, na ordem que você queria.
A diferença é que você está criando uma função anônima na hora para depois dar callback, e um extra. Quando só usa o identificador, não deixa de ser uma função, não anônima, e que depois pode ser dado um callback, porque em JS para chamar uma função não importa com oa função foi declarada.
Tudo é que estão de onde está sendo executado. Em resumo, com parêntese chama, sem ele não chama, é só um objeto. Só lembrando que function()
é uma declaração e não uma chamada, é uma palavra-chave com significado especial.
Se você mandar executar com um debugger ligado, mandando executar linha por linha ajuda entender o funcionamento.
Uma das coisas que faz as pessoas se darem mal no Stack Overflow é que elas não colocam o MCVE, que é uma das coisas mais básicas da programação. Então não é o iniciante que se dá mal lá, é quem é iniciante sem agir como iniciante. Por que estou falando disso? Porque seu exemplo não faz o MCVE. Ele coloca ruído, coloca coisas que nada tem a ver com o problema. E aí fica difícil de entender.
Em alguns casos porque está pulando etapas. O que torna difícil entender algumas coisas. Porque está agindo como se fosse um programador que já abe coisas mais avançadas (que muitas pessoas vão dizer que ele é sênior, mas isso nada tem a ver com senioridade). Inclusive o sênior aprende que não pode pular etapas.
Aprender debugar é uma das coisas mais básicas e importantes que um estudante de programação deve fazer, até para aprender a entedner oque está fazendo. Não é uma técnica avançada ("de sênior"). E muita gente hoje faz códigos complexos (em geral com ^C^V) e não sabem depurar um código.
Daí começo responendo que aprendi por livros, com a vida, talvez tenha ajudado em algum momento em algum site, porque quando eu aprendi não existiam sites, mas cosias mais avançadas foram alguns. Eu aprendo de forma estruturada, em uma ordem que faça sentido. Demora. Parece chato, você não vê resultados bonitinhos na hora, até desanima quem precisa demais de estímulos. Mas é a forma correta para aprender corretamente. Eu sei que muita gente não quer, ou até não consegue segurar o ímpeto de ir logo para o que estimula mais, mas prejudica todo futuro.
Muitas vezes falta a pessoa seguir os links fornecidos. Geralmente os links que eu coloco são clicados por menos de 20% das pessoas, alguns casos bem menos. Claramente a pessoa está fugindo do aprendizado (não deveria ser perto de 100% porque muitas pessoas não precisam clicar mesmo).
Se está perdido com os termos é porque os pulou. Então fica difícil explicar corretamente. Alguns explicam e fica torto, porque para simplificar acaba ensinando errado. Tem que voltar atrás. Tem que entender precedência e como o código será executado, antes de tentar aprender callback, ou eventos. Tem que aprender que apesar da linguagem aceitar não usar ;
, tem caso que dá problemas, e que ela não foi bem pensada para funcionar assim, então é melhor usar sempre, mesmo que algumas pessoas ensinam assim para fazer graça. E são as mesmas pessoas que pregam legibilidade que fazem assim, mostrando que sequer entendo o que é legível, o que te faz ficar pensando sobre o que deveria ser irrelevante, ou ter erros "estranhos" quando pega a exceção.
Não vai entedner essas coisas se não souber oque é variável, objeto, função, como funcionam, parâmetros, argumentos, precedência, controle de fluxo, outros elementos do código, fazer teste de mesa, callback, avançado, valor e referência, etc. Só passei algumas coisas, tem muito mais material, lá e em outros lugares como a Wikipedia, C2 wiki, o SOen, etc. Sem entrar em asssuntos específicos foram os 3 que mais me ajudaram. Além de documentação para aprender tecnologia (não aprender a programar).
Parece bobo ou avançado, mas ajuda entender. Também.
Eu sou visto com o chato que fala em estudar os fundamentos, não pular etapas, de aprender programar corretamente, que programação é matemática, lógica, mas justamente porque as pessoas ignoram que está tão difícil preencher as vagas. Eu não sei mais o que fazer para fazer as pessoas apreciarem o que importa. Quem estuda corretamente não teme ChatGPT ou diminuição do número de vagas. Espero que isso ajude mais que só dizer porque funciona ou não, que nem sei se consegui fazer entender.
Aprendizado é um processo, é uma maratona. Ou várias. É uma vida. Quem tenta correr como se fosse 100mts rasos chama-se coelho, e para no meio do caminho, ou chega muito lá atrás. Dá trabalho? É cansativo? Sim. Talvez dancinha no TikTok seja mais fácil.
Se preocupar com esse ponto já é acima da média, muita gente só quer ver o resultado. Por iss oeu gastei meu tempo respondendo, acho que posso ajudar o OP e outros, se não explicando bem, pelo menos tentando colocar no caminho certo.
Faz sentido para você?
Espero ter ajudado.
Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente. Para saber quando, me segue nas suas plataformas preferidas. Quase não as uso, não terá infindas notificações (links aqui).