Callbacks em Javascript

Eu as vezes tenho o mau-hábito de pensar que o que eu sei todos sabem e acabo menosprezando certos assuntos interessantes por causa disso.
Já ouviu falar em callbacks ? Se você trabalha com Javascript com certeza já ouviu, mas você sabe o que são e para que servem ?
jQuery usa muito, mas se você não sabe o que são eu explico =D.

Digamos que você precise de uma função que crie uma div após 5 segundos numa tela. É besta, mas é apenas um exemplo…
Só que essa função é muito útil para você e toda hora você precisa usá-la. Então você arruma ela, deixa ela o mais simples possível porque parece ser uma solução boa para aquele propósito.

Mas você acaba notando que aquela solução tem que ser modificada toda hora para atender aos requisitos de cada projeto. Como assim?
Eu tenho essa função de criar div a cada 5 segundos, bonitona:

function div(target, conteudo){
	setTimeout(function(){
	
	var div = document.createElement("div")
	div . innerHTML = conteudo
	target . appendChild( div )
	
	}, 5000)
}

Mas no outro projeto eu só quero trocar a classe da div. Eu teria de reescrever a função de novo:

function div(target, classe){
	setTimeout(function(){

	var div = document.createElement("div")
	div . className = classe
	target . appendChild( div )

	}, 5000)
}

Toda hora reescrever a função que faz a mesma coisa… que idiotice né ? Bom, nesse caso, é uma boa prática usar callbacks.
Callbacks nada mais são do que funções que são passadas como argumento de uma outra função. Vou explicar melhor.
Se eu fizer:

function f( variavel ){
	alert( variavel )
}
var a = 5
f(a) 

Vai me dar um alert mostrando 5.
Se eu fizer:

function f2(){
	alert( " Função 2 " )
}

f(f2)

O alert vai me retornar o protótipo da função, ou seja, a função foi passada para a outra função normalmente.
Só, que para se executar a função é necessário sempre usar o parênteses.
A função f não vai chamar a função f2, só vai mostrar o corpo dela. Como eu faria para chamar a função f2 na f?

Simples, só chamá-la na f:

function f( funcao ){
	funcao()
}

Pronto, agora, se eu fizer:

f( f2 )

O alert vai mostrar “Função 2”. Ou seja, a f2 foi passada como argumento e a f pega essa função,
usa uma nova variável chamada “funcao” e faz a chamada dela.
Sabendo disso, daria para nós generalizarmos a nossa primeira função utilizando esse conceito.

function div(target, callback){	
	setTimeout(function(){
		var div = document.createElement("div")
		target . appendChild( div )
		callback?callback(div):null
	}, 5000)
}

A função continua criando uma div e colocando num alvo qualquer após 5 segundos,
porém na última linha do seu código eu verifico se a variável callback está setada, ou seja, se foi passado algo como segundo argumento.
Se eu passar uma função a f vai fazer o seguinte:

callback = funcao

Agora a função está generalizada e callback é uma função qualquer.
Se eu quiser criar um div após 5 segundos e adicionar uma classe nele eu faria:

div( 

	document.body, 

	function(div){
		div.className = "Classe"
	}
)

Se eu quiser criar uma div e botar um conteúdo ao invés de colocar uma classe eu faria:

div(

	document.body,
	
	function(div){
		div.innerHTML = " Meu conteúdo"
	}
)

De novo, para entender, o que a f vai fazer nesse último caso é :

target = document.body
callback = function (div){ div.innerHTML = "Meu conteúdo"}

E então chamará:

callback(div)

O argumento “div” diz respeito ao elemento criado na função div(), pois é na função div() que eu faço a chamada do callback.

**Callbacks utilizam conceitos como programação funcional e closures. Mas isso é assunto para outro post 😉

Eu utilizei esse exemplo para demonstrar dois casos interessantes onde as callbacks são usadas.
Um caso é quando queremos generalizar uma função para utilizá-la de várias formas diferentes sem precisar mexer nela.

Outro caso é quando não sabemos quando algo termina para poder começar outro.
Por exemplo, eu uso uma função que faz um efeito de fadeOut (sumindo elemento aos poucos) , mas após esse efeito,
queria a mostrar um alert na tela. Como você sabe que terminou o efeito para poder chamar outra função? Agora você já sabe !!!

OnReady, em javascript, sem uso de frameworks.

Para quem trabalha desenvolvendo sites de forma não obstrutiva, já precisou usar  muitas vezes o window.onload, certo?

Isso não tem nenhum problema de compatibilidade entre os browsers ou algo do tipo, mas tem um incoveniente. O script só é executado quando todos os elementos E imagens serem carregados.

Geralmente o que deixa um site pesado são as imagens, no caso de uma tela pesada com muitas imagens ou imagens grandes (em kb), o tempo de carregamento aumenta muito e muitas vezes o script não pode esperar tanto tempo.

Os frameworks já implementam esse onready, como o jQuery por exemplo.
Mas e se não quiser usar um framework no seu projeto ?

Há algumas soluções prontas na internet, já vi algumas muito boas como a do Micox.

Uma vez enquanto eu desenvolvia um projeto, pensei numa solução bem simples eacabei usando em alguns projetos sem frameworks, gostaria de compartilhar com vocês.

A minha idéia é a seguinte:

function ready(fn){
    var clock = setInterval(
        function(){
            if(document.body) {
                fn()
                return clearInterval(clock)
            }

        },1
    )
}

 

A idéia é repetir uma função indefinidas vezes testando se existe o objeto document.body.

Teóricamente só existirá o objeto quando todos os seus filhos tiverem sido criados.
No caso de existir, a função termina de executar o teste e executa uma função passada como parâmetro.

Embora a idéia de repetir algo infinitas vezes pareça um pouco intimidadora, penso que um possível loop infinito aconteça no caso do body não ter sua tag fechada (mas aí também né…), mas nunca testei isso.

Fiz alguns testes com alert para saber quantas vezes a função é executada e na maioria dos casos só repetia uma vez. Os objetos parecem ser criados muito rapidamente, antes do segundo milissegundo da repetição da função.

A forma de usar seria essa:

	ready(function(){
		alert( " Carreguei =) " )
	})

Dentro da função você colocaria qualquer comando ou outra função…

É essa minha idéia, se alguém achar algo de ruim poste, para tentarmos melhorar essa estratégia.

Aquele abraço.