Explicação das promessas de JavaScript: então, catch, Promise.all e erros comuns

  1. Início
  2. Blog
  3. Desenvolvimento de software

Desenvolvimento de Software

Promessas de JavaScript explicadas: então, catch, Promise.all e erros comuns

As promessas são como o JavaScript lida com o trabalho que termina mais tarde, e o async/await é construído sobre elas. Aprenda como eles realmente se comportam, com resultados reais de cada exemplo, e evite os erros que engolem seus erros.

10 min de leitura
Explicação das promessas de JavaScript: then, catch, Promise.all e erros comuns

Principais pontos

  • Uma promessa é um objeto que representa um resultado que estará disponível posteriormente.
  • Fica pendente até ser cumprido ou rejeitado e, uma vez liquidado, nunca muda.
  • Cada then retorna uma nova promessa, que é o que torna possível o encadeamento.
  • Use Promise.all quando todos os resultados forem necessários e Promise.allSettled quando desejar todos os resultados.

Solicitações de rede, leituras de arquivos e temporizadores terminam mais tarde, mas o JavaScript não pode simplesmente parar e esperar, ou a página congelaria. As promessas do JavaScript oferecem uma maneira organizada de dizer “quando isso terminar, faça aquilo e, se falhar, faça isso”.

Mesmo que você escreva principalmente async e await, você estará usando promessas subjacentes, portanto, entendê-las torna a depuração muito mais fácil. Executamos esses exemplos no Node.js 24 e a saída mostrada vem dessa execução. Eles se comportam da mesma maneira nos navegadores modernos.

O que é uma promessa?

Uma promessa é um espaço reservado para um valor futuro. Pense na campainha que um restaurante lhe dá quando você faz um pedido. Você ainda não tem sua comida, mas segura algo que lhe dirá quando estiver pronta ou que há um problema, e enquanto isso você está livre para fazer outras coisas.

No código, uma função assíncrona retorna uma promessa imediatamente e você anexa o que deve acontecer a seguir:

fetch("/api/user")
  .then((response) => response.json())
  .then((user) => console.log(user.name))
  .catch((error) => console.log("Failed:", error.message));

Os três estados prometidos

Estado Significado O que funciona
Pendente O trabalho não terminou Nada ainda
Cumprido Terminou com um valor then manipuladores
Rejeitado Falhou por um motivo catch manipuladores

Uma promessa que é cumprida ou rejeitada é chamada de resolvida. Uma promessa só pode ser cumprida uma vez. Depois disso, seu estado e valor são fixos e as chamadas posteriores para resolve ou reject são ignoradas.

Crie uma promessa

Você normalmente consome promessas de APIs como fetch, mas criar uma mostra como elas funcionam. O construtor recebe duas funções, resolve e reject:

function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, name: "Asha" });
      } else {
        reject(new Error("Invalid id"));
      }
    }, 100);
  });
}

console.log(fetchUser(1));

// Promise { <pending> }

Registrar a promessa imediatamente mostra que ela ainda está pendente, porque o cronômetro não foi acionado. O valor só pode ser alcançado através de um manipulador ou await. Sempre rejeite com um objeto Error em vez de uma string, pois um Error carrega um rastreamento de pilha que ajuda na depuração.

então, pegue e finalmente

Esses três métodos vinculam manipuladores a uma promessa:

fetchUser(1).then((user) => console.log(user.name));
// Asha

fetchUser(-1)
  .then((user) => console.log("never runs", user))
  .catch((error) => console.log("caught:", error.message))
  .finally(() => console.log("finally"));

// caught: Invalid id
// finally

then é executado em caso de sucesso, catch é executado em caso de falha e finally é executado de qualquer maneira. Isso torna finally o lugar certo para limpeza, como ocultar um botão giratório de carregamento. Não recebe argumentos, porque não deveria se preocupar com o resultado.

Encadeando promessas

A ideia principal é que then sempre retorna uma nova promessa. Qualquer que seja o retorno do seu manipulador, torna-se o valor dessa nova promessa:

Promise.resolve(2)
  .then((n) => n * 10)
  .then((n) => n + 1)
  .then((n) => console.log("chain:", n));

// chain: 21

Se um manipulador retornar outra promessa, a cadeia espera que ela seja resolvida antes de prosseguir. Isto é o que transforma retornos de chamada aninhados em uma sequência simples de etapas:

getUser(id)
  .then((user) => getOrders(user.id))
  .then((orders) => getInvoice(orders[0].id))
  .then((invoice) => console.log(invoice.total))
  .catch(handleError);

Como os erros viajam através de uma cadeia

Uma promessa rejeitada pula cada then até chegar a catch. Jogar dentro de um manipulador também conta como rejeição. E um catch que retorna normalmente coloca a cadeia de volta nos trilhos:

Promise.resolve("start")
  .then(() => {
    throw new Error("step 2 failed");
  })
  .then(() => console.log("skipped"))
  .catch((error) => {
    console.log("caught:", error.message);
    return "recovered";
  })
  .then((value) => console.log("continues:", value));

// caught: step 2 failed
// continues: recovered

Pense nisso como a versão assíncrona de try e catch. Um catch no final geralmente cobre toda a cadeia. Se você deseja apenas registrar um erro e ainda assim falhar, lance-o novamente de dentro do catch.

Dica: fetch rejeita apenas falhas de rede. Uma resposta 404 ou 500 ainda é válida, então verifique response.ok você mesmo. Nossa folha de referências de códigos de status HTTP explica o que cada código significa.

Promise.all, allSettled, raça e qualquer

Esses métodos estáticos coordenam várias promessas ao mesmo tempo. Todos eles pegam um array e retornam uma promessa.

Método Cumpre quando Rejeita quando Use-o para
Promise.all Todos cumprem Qualquer um rejeita Todos os resultados são obrigatórios
Promise.allSettled Todos se acomodam Nunca Você quer todos os resultados
Promise.race O primeiro a resolver cumpre O primeiro a resolver rejeitos Tempos limite
Promise.any O primeiro cumpre Todos rejeitam A primeira fonte de sucesso

Para testá-los, usamos dois pequenos ajudantes, um que funciona após um atraso e outro que falha após um atraso:

const wait = (ms, value) =>
  new Promise((resolve) => setTimeout(() => resolve(value), ms));
const fail = (ms, message) =>
  new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms));

Promise.all

await Promise.all([wait(300, "a"), wait(100, "b"), wait(200, "c")]);

// [ 'a', 'b', 'c' ]   took about 300 ms

Dois detalhes a serem observados. Os resultados mantêm a ordem de entrada, mesmo que b tenha terminado primeiro. E o tempo total foi de cerca de 300 ms, o tempo da tarefa mais lenta e não a soma dos 600 ms, pois as três já estavam rodando ao mesmo tempo. Se algum deles rejeita, a coisa toda rejeita imediatamente:

await Promise.all([wait(100, "a"), fail(50, "b failed")]);

// rejected: b failed

Promise.allSettled

Este espera por tudo e reporta cada resultado, para que uma falha não perca o resto. É adequado para um painel onde cada widget é carregado de forma independente:

const results = await Promise.allSettled([wait(50, "a"), fail(50, "b failed")]);
results.map((r) => r.status);

// [ 'fulfilled', 'rejected' ]

Promessa.corrida e Promessa.qualquer

await Promise.race([wait(300, "slow"), wait(100, "fast")]);   // fast
await Promise.any([fail(50, "x"), wait(100, "ok")]);          // ok

A diferença é que race é o primeiro a resolver, mesmo que tenha falhado, enquanto any ignora as falhas desde que algo dê certo. Um uso clássico de race é adicionar um tempo limite a uma solicitação lenta, embora a tarefa mais lenta continue sendo executada em segundo plano, a menos que você a cancele.

Promessas e assíncrono/espera

async e await são uma forma mais legível de trabalhar com as mesmas promessas. Uma função async sempre retorna uma promessa e await pausa essa função até que uma promessa seja estabelecida:

async function showInvoice(id) {
  try {
    const user = await getUser(id);
    const orders = await getOrders(user.id);
    console.log(orders.length);
  } catch (error) {
    console.log("Failed:", error.message);
  }
}

As rejeições tornam-se erros lançados, então try e catch comuns funcionam. Você ainda precisa de Promise.all para trabalho paralelo, porque aguardar uma chamada após a outra as executa em sequência. Nosso guia JavaScript assíncrono/aguardado se aprofunda nisso.

Promessas e o ciclo de eventos

Os manipuladores de promessa nunca são chamados imediatamente, mesmo quando a promessa já foi cumprida. Eles são executados após o término do código atual e antes de qualquer temporizador. Aqui está a prova:

setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("microtask"));
console.log("sync");

// sync
// microtask
// timeout

O código síncrono é executado primeiro. Em seguida vem a fila de microtarefas, que contém manipuladores de promessas. Só então o loop de eventos seleciona a próxima tarefa, como um cronômetro. Esta é uma pergunta popular em entrevistas e explica por que um setTimeout igual a zero não é realmente imediato.

Promessas versus retornos de chamada

Preocupação Retornos de chamada Promessas
Sequenciamento Aninhado, mais profundo em cada etapa Uma corrente plana
Erros Verificado em todos os níveis Um catch pode cobrir a corrente
Trabalho paralelo Contadores manuais Promise.all
Ligou duas vezes por engano? Possível Não é possível resolver duas vezes

Erros comuns

  • Esquecendo de retornar para dentro de then. Sem um return, o próximo passo recebe undefined e não espera pela sua promessa interior.
  • Não catch. Isso causa uma rejeição não tratada e, no Node.js, isso interrompe o processo por padrão.
  • Aninhamento em vez de encadeamento. Colocar then dentro de then recria o inferno de retorno de chamada. Devolva a promessa e continue a corrente.
  • Envolvendo uma promessa em new Promise. Se uma função já retorna uma promessa, basta usá-la.
  • Usando forEach com retornos de chamada assíncronos. Não espera. Use for...of ou Promise.all com map, conforme mostrado em nosso guia para métodos de array JavaScript.
  • Pensar que uma promessa pode ser cancelada. Não pode por si só. Para solicitações, use AbortController.

Considerações finais

As promessas se resumem a algumas regras: uma promessa é estabelecida uma vez, then retorna uma nova promessa e os erros percorrem a cadeia até que algo os detecte. Uma vez que essas regras pareçam naturais, async e await deixam de ser mágicas. Para praticar, reescreva uma função baseada em retorno de chamada usando promessas, depois busque dois pontos de extremidade com Promise.all e trate uma falha adequadamente.

Perguntas frequentes

O que é uma promessa em JavaScript?

Uma promessa é um objeto que representa o resultado final do trabalho assíncrono. Começa como pendente e depois cumpre com um valor ou rejeita com um motivo.

Qual é a diferença entre then e catch?

então é executado quando uma promessa é cumprida e o catch é executado quando é rejeitado. catch é efetivamente apenas com um manipulador de rejeição e também pode capturar erros lançados em etapas anteriores.

Qual é a diferença entre Promise.all e Promise.allSettled?

Promise.all rejeita assim que uma promessa é rejeitada. Promise.allSettled sempre espera por todos eles e informa o status e o valor ou motivo de cada um.

Devo usar promessas ou assíncrono/aguardar?

Eles trabalham juntos, pois a espera se baseia em promessas. A maior parte do código é melhor lida com async/await, enquanto Promise.all e métodos semelhantes permanecem úteis para trabalho paralelo.

As promessas acontecem em paralelo?

O JavaScript executa um trecho de código por vez em um thread, mas funciona como se as solicitações de rede acontecessem em segundo plano. Vários deles podem estar em andamento ao mesmo tempo.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Teste de software

Teste de aceitação do usuário (UAT): processo, lista de verificação e exemplos reais

Explicação do teste de aceitação do usuário: quem o executa, como ele difere do controle de qualidade, um processo de sete etapas, um modelo de caso…

Leia o artigo →
Bancos de dados e SQL

Valores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas

Valores SQL NULL explicados com consultas que executamos: lógica de três valores, IS NULL, COALESCE, NULLIF, como agregações, junções e classificaçã…

Leia o artigo →
Bancos de dados e SQL

Instrução SQL CASE WHEN: sintaxe, exemplos reais e erros comuns

SQL CASE WHEN explicado com consultas que executamos: formulários simples e pesquisados, ordem de condição, pivôs com SUM, truques COUNT, classifica…

Leia o artigo →
Desenvolvimento de software

O que é JWT? JSON Web Tokens explicados com um exemplo prático

O que é JWT? JSON Web Tokens explicados: as três partes, um token construído e adulterado no código que executamos, JWT vs sessões, armazenamento, t…

Leia o artigo →
Desenvolvimento de software

Loop de eventos JavaScript explicado: pilha de chamadas, microtarefas e temporizadores com exemplos

O loop de eventos JavaScript explicado com o código que executamos: pilha de chamadas, filas de tarefas e microtarefas, por que setTimeout 0 não é i…

Leia o artigo →
Desenvolvimento de software

Geradores Python e rendimento explicado: economize memória com exemplos reais

Geradores Python e rendimento explicados com o código que executamos: como eles pausam e reiniciam, medem economia de memória, pipelines, sequências…

Leia o artigo →
Inteligência artificial

O que são incorporações? Similaridade de vetores e cossenos explicada de forma simples

O que são incorporações? Um guia em linguagem simples para vetores, similaridade de cossenos e pesquisa semântica, com um pequeno exemplo que execut…

Leia o artigo →
DevOps

Implantação azul-verde vs canário: diferenças, compensações e como escolher

Explicação da implantação azul-verde versus canário: como cada um funciona, custo, risco e reversão, agrupamento estável de usuários que testamos, m…

Leia o artigo →
Automação de testes

Testes instáveis: o que os causa e como corrigi-los para sempre

Testes instáveis explicados com demonstrações que executamos: as principais causas, como confirmar e diagnosticar uma, soluções adequadas para tempo…

Leia o artigo →
Teste de software

Teste de carga versus teste de estresse: diferenças, métricas e exemplos k6

Teste de carga versus teste de estresse explicado: o que cada um prova, testes de pico e absorção, as métricas que importam, por que as médias engan…

Leia o artigo →
Teste de software

Caso de teste versus cenário de teste: principais diferenças, exemplos e quando usar cada um

Caso de teste vs cenário de teste explicado: definições claras, uma tabela lado a lado, um recurso de redefinição de senha retirado de cenários para…

Leia o artigo →
Bancos de dados e SQL

Otimização de consultas SQL: leia planos de execução e corrija consultas lentas, com intervalos

Otimização de consulta SQL testada em um milhão de linhas: leia planos de execução, adicione os índices corretos, corrija filtros não sargáveis, use…

Leia o artigo →
Bancos de dados e SQL

SQL UNION vs UNION ALL: diferenças, exemplos e desempenho testado

SQL UNION vs UNION ALL explicado com consultas que executamos: duplicatas, as regras que ambos seguem, um bug de dados real, desempenho medido, INTE…

Leia o artigo →
Desenvolvimento de software

Tutorial da API JavaScript Fetch: GET, POST, tratamento de erros e tempos limite

Um tutorial da API JavaScript Fetch testado em um servidor local: GET e POST, por que 404 não lança, um auxiliar reutilizável, strings de consulta, …

Leia o artigo →
Desenvolvimento de software

Manipulação de arquivos Python: leitura, gravação, CSV, JSON e pathlib com exemplos

Manipulação de arquivos Python explicada com código que executamos: modos abertos, com, leitura de arquivos grandes, CSV, JSON, pathlib, arquivos bi…

Leia o artigo →
Desenvolvimento de software

Classes e objetos Python: tutorial OOP com exemplos reais

Classes e objetos Python explicados com código que executamos: __init__, métodos, atributos de classe vs instância, herança, propriedades, métodos d…

Leia o artigo →
Inteligência artificial

O que é um modelo de linguagem grande? Como funcionam os LLMs, explicado sem exageros

O que é um modelo de linguagem grande? LLMs explicados em linguagem simples: tokens, incorporações, atenção, treinamento, temperatura e janelas de c…

Leia o artigo →
DevOps

Comandos Linux para iniciantes: o guia essencial do terminal com exemplos

Comandos Linux para iniciantes, agrupados por tarefa: navegação, arquivos, visualização de logs, grep, pipes, permissões, processos e arquivos, com …

Leia o artigo →
Automação de testes

Selenium vs Playwright: principais diferenças, código comparado e qual escolher

Selenium vs Playwright comparado: arquitetura, espera, suporte a navegador e linguagem, depuração, o mesmo teste em ambas as ferramentas, conselhos …

Leia o artigo →
Teste de software

Particionamento de equivalência: técnica de design de teste explicada com exemplos resolvidos

Explicação do particionamento de equivalência: como encontrar partições válidas e inválidas, exemplos resolvidos para idades, senhas e cupons, teste…

Leia o artigo →
Teste de software

Gravidade vs Prioridade em Testes de Software: Diferenças, Matriz e Exemplos Reais

Explicação da gravidade versus prioridade em testes de software: definições claras, uma matriz de quatro vias com exemplos, escalas de nível, quem d…

Leia o artigo →
Bancos de dados e SQL

Tutorial de subconsultas SQL: tipos, subconsultas correlacionadas e EXISTS com exemplos

Um tutorial de subconsultas SQL com consultas que executamos: subconsultas escalares, IN, FROM e SELECT, subconsultas correlacionadas, EXISTS, a arm…

Leia o artigo →
Bancos de dados e SQL

Transações SQL e ACID explicados: COMMIT, ROLLBACK e níveis de isolamento

Transações SQL e ACID explicadas com uma transferência bancária que executamos: COMMIT, ROLLBACK, savepoints, locks, níveis de isolamento, leituras …

Leia o artigo →
Desenvolvimento de software

Folha de dicas dos comandos do Git: os comandos que você realmente usa, com exemplos

Uma folha de dicas de comandos Git agrupada por tarefa: configuração, preparação, ramificações, controles remotos, stash, histórico e desfazer erros…

Leia o artigo →
Desenvolvimento de software

Explicação das promessas de JavaScript: então, catch, Promise.all e erros comuns

Promessas JavaScript explicadas com o código que executamos: estados, cadeias then e catch, tratamento de erros, Promise.all, allSettled, race e any…

Leia o artigo →
Desenvolvimento de software

Tutorial do dicionário Python: métodos, exemplos e padrões comuns

Um tutorial de dicionário Python com código que executamos: criar, ler e atualizar dictos, métodos-chave, mesclagem, compreensão, padrões de contage…

Leia o artigo →
Inteligência artificial

O que é RAG? Geração aumentada de recuperação explicada com um exemplo prático

O que é RAG? Geração aumentada de recuperação explicada passo a passo: indexação, chunking, embeddings, recuperação, um pequeno exemplo funcional de…

Leia o artigo →
DevOps

Tutorial Terraform para iniciantes: infraestrutura como código do primeiro arquivo aos módulos

Um tutorial do Terraform para iniciantes: comece sem conta na nuvem, aprenda como iniciar, planejar e aplicar, depois variáveis, saídas, estado, mód…

Leia o artigo →
DevOps

Docker vs Kubernetes: principais diferenças, como eles funcionam juntos e o que você precisa

Docker vs Kubernetes explicado: o que cada um faz, o mesmo aplicativo rodando com ambos, Docker Compose e Swarm comparados, a mudança do dockershim …

Leia o artigo →
Automação de testes

Tutorial Pytest para iniciantes: luminárias, parametrização e exemplos reais

Um tutorial pytest para iniciantes com testes que executamos no pytest 9.2: asserções, fixtures, parametrizar, conftest, marcadores, tmp_path, monke…

Leia o artigo →
Teste de software

Ciclo de vida de teste de software (STLC): 6 fases, critérios de entrada e saída, exemplo

O ciclo de vida de teste de software explicou: seis fases do STLC com critérios de entrada e saída, resultados, um exemplo de recurso de login funci…

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →