Padrões de concorrência
Não-determinismo
Considere a função chamaDepois(f), que chama a função f após um intervalo de tempo aleatório:
Essa função emula o comportamento de um download ou de um clique do mouse: não podemos prever quando esses eventos acontecerão.
Agora considere que as funções de adicionar e multiplicar números foram implementadas em um computador remoto, e precisamos fazer requisições para esses computadores para realizar as operações. Eis um exemplo prático de programa concorrente no qual a ordem de execução das tarefas afeta o resultado:
Essa solução possui dois problemas:
- não temos como saber a ordem em que as funções serão executadas
- não temos como saber quando as funções terminaram de executar
- estamos chamando
imprimeapós 2000 milissegundos, mas quem garante que esse tempo é suficiente?
- estamos chamando
Para resolver isso, usamos callbacks: chamamos a função assíncrona passando uma função que será “chamada de volta” (called back) quando a função assíncrona terminar de executar.
Estrutura sequencial com callbacks
Usamos callbacks para garantir uma ordem sequencial de execução de funções assíncronas:
Qual será a ordem de textos exibidos no console?
À medida que combinamos sequencialmente várias funções assíncronas, o nível de indentação do programa vai aumentando, um problema conhecido como pyramid of doom, callback hell ou mesmo hadouken indentation:

No exemplo abaixo, o que será mostrado no console?
Join
Agora considere a situação em que há duas funções assíncronas, adiciona1 e adiciona2, que adicionam 1 e 2, respectivamente, à variável x. Queremos executar as duas funções e, ao final, executar imprime para ver o resultado.
function adiciona1(callback) {
chamaDepois(function () {
console.log('adiciona');
x = x + 1;
callback();
});
}
function adiciona2(callback) {
chamaDepois(function () {
console.log('adiciona');
x = x + 2;
callback();
});
}
function imprime() {
console.log(x);
}
A diferença aqui é que, dentre adiciona1 e adiciona2, não importa quem executa primeiro, contanto que imprime só execute quando as ambas terminarem de executar:
adiciona1 ---
\_____ imprime
/
adiciona2 ---
Esse padrão de concorrência é conhecido como join. Eis uma forma de implementá-lo:
Desafio
Na solução anterior, usamos variáveis globais e colocamos a chamada a imprime no próprio código das funções adiciona1 e adiciona2. Tente generalizar essa solução:
- Crie uma função
join(funcoes, callback), que recebe um array de funções (funcoes) e uma função (callback) - Cada função do array recebe como parâmetro uma função
f, e chamafquando termina de executar - A função
joinchamacallbackquando todas as funções emfuncoesterminam de executar.
Exercícios
Nesta aula vamos usar o site JSONPlaceholder para dar suporte a nossos exemplos práticos.
Para simplificar o código, definimos a variável global ROOT como sendo a URL do JSONPlaceholder.
As funções $.get(url, f) e $.post(url, f) realizam uma requisição AJAX à url e, quando a resposta chega, chama a função f passando o objeto associado à resposta.
Para ver o que a chamada $.get(ROOT + '/users', ...) retorna, basta acessar https://jsonplaceholder.typicode.com/users no seu navegador.
Exemplo simples
Antes de executar o código a seguir, tente prever a ordem de execução das instruções console.log.
Algo mais elaborado
O exemplo a seguir deveria buscar o id do usuário com username Delphine e então mostrar o título dos álbuns desse usuário:
No entanto, o programador não percebeu que as chamadas de função que realizou são assíncronas, e por isso o programa não funciona como deveria. Conserte o programa.
Join
Agora você quer pegar o e-mail de todos os usuários pagantes e então exibir os e-mails separados por vírgulas (não importa a ordem). (Suponha que a url /users, que retorna todos os dados de todos os usuários, não existe). Corrija o código abaixo: