Async
Escopo: JavaScript. Idiomas específicos deste ecossistema.
Toda operação que depende de I/O (Input/Output, Entrada/Saída) é assíncrona. Bloquear o thread principal trava a aplicação.
Conceitos fundamentais
| Conceito | O que é |
|---|---|
| I/O (Input/Output, Entrada/Saída) | Operação que atravessa o limite do processo: rede, disco, banco |
| callback (função de retorno) | Função passada como argumento para executar quando a operação termina |
| Promise (promessa de valor) | Objeto que representa o resultado futuro de uma operação assíncrona |
| API (Application Programming Interface, Interface de Programação de Aplicações) | Contrato público de uma biblioteca ou serviço externo |
Callback hell
❌ Ruim: aninhamento cresce sem controle
function fetchUserData(id, callback) {
getUser(id, (user) => {
getOrders(user.id, (orders) => {
getInvoices(orders[0].id, (invoices) => {
callback({ user, orders, invoices });
});
});
});
}
✅ Bom: async/await, linear e legível
async function fetchUserData(id) {
const user = await getUser(id);
const orders = await getOrders(user.id);
const invoices = await getInvoices(orders[0].id);
const userData = { user, orders, invoices };
return userData;
}
.then() encadeado
❌ Ruim: verboso, difícil de depurar
function fetchUserData(id) {
return getUser(id)
.then((user) => getOrders(user.id).then((orders) => ({ user, orders })))
.then(({ user, orders }) =>
getInvoices(orders[0].id).then((invoices) => ({ user, orders, invoices }))
);
}
✅ Bom: mesmo resultado, sem o ruído
async function fetchUserData(id) {
const user = await getUser(id);
const orders = await getOrders(user.id);
const invoices = await getInvoices(orders[0].id);
const userData = { user, orders, invoices };
return userData;
}
Bloqueio síncrono
❌ Ruim: loop síncrono trava o thread principal
function wait(ms) {
const end = Date.now() + ms;
while (Date.now() < end) {} // congela tudo durante ms milissegundos
}
wait(3000); // aplicação trava por 3 segundos
✅ Bom: Promise libera o thread enquanto aguarda
function wait(ms) {
const timer = new Promise((resolve) => setTimeout(resolve, ms));
return timer;
}
async function run() {
await wait(3000);
console.log("done");
}
Promise.all: execução paralela
Quando as operações são independentes entre si, rodá-las em paralelo reduz o tempo total de espera.
❌ Ruim: await sequencial quando não há dependência
async function fetchDashboard(userId) {
const orders = await fetchOrders(userId); // espera terminar
const invoices = await fetchInvoices(userId); // só começa depois
const profile = await fetchProfile(userId); // só começa depois
const dashboard = { orders, invoices, profile };
return dashboard;
}
✅ Bom: Promise.all dispara tudo ao mesmo tempo
async function fetchDashboard(userId) {
const requests = [
fetchOrders(userId),
fetchInvoices(userId),
fetchProfile(userId),
];
const [orders, invoices, profile] = await Promise.all(requests);
const dashboard = { orders, invoices, profile };
return dashboard;
}
Use
Promise.allquando as operações não dependem umas das outras. Se uma falhar, todas falham. UsePromise.allSettledquando quiser continuar mesmo com erros parciais.
API client centralizado
Um único cliente carrega a configuração base. Os módulos recebem o cliente por injeção: sem fetch solto espalhado pelo código.
❌ Ruim: fetch direto, configuração duplicada em todo lugar
// user.service.js
async function fetchUser(id) {
const response = await fetch(`https://api.example.com/users/${id}`, {
headers: { Authorization: `Bearer ${token}` },
});
const user = await response.json();
return user;
}
// order.service.js
async function fetchOrders(userId) {
const response = await fetch(`https://api.example.com/orders?userId=${userId}`, {
headers: { Authorization: `Bearer ${token}` },
});
const orders = await response.json();
return orders;
}
✅ Bom: cliente único, injetado onde precisar
// api.client.js
function createApiClient(baseUrl, token) {
async function get(path) {
const fetchConfig = { headers: { Authorization: `Bearer ${token}` } };
const response = await fetch(`${baseUrl}${path}`, fetchConfig);
const body = await response.json();
return body;
}
const client = { get };
return client;
}
export const apiClient = createApiClient("https://api.example.com", token);
// user.service.js
async function fetchUser(apiClient, id) {
const user = await apiClient.get(`/users/${id}`);
return user;
}
// order.service.js
async function fetchOrders(apiClient, userId) {
const orders = await apiClient.get(`/orders?userId=${userId}`);
return orders;
}
Quando criar uma função async
❌ Ruim: I/O síncrono bloqueia o event loop
// banco de dados síncrono: não existe, mas ilustra o padrão errado
function findUser(id) {
const user = database.querySync("SELECT * FROM users WHERE id = $1", [id]);
return user;
}
// leitura de arquivo síncrona trava o processo
function readConfig() {
const config = fs.readFileSync("./config.json", "utf-8");
return config;
}
✅ Bom: toda operação de I/O é async
// banco de dados
async function findUser(id) {
const user = await database.query("SELECT * FROM users WHERE id = $1", [id]);
return user;
}
// API externa
async function fetchRates() {
const response = await fetch("https://api.example.com/rates");
const rates = await response.json();
return rates;
}
// leitura de arquivo
async function readConfig() {
const config = await fs.promises.readFile("./config.json", "utf-8");
return config;
}
Desenvolvido por @thiagocajadev · Fork baseado no repositório pmndrs/docs · Poimandres.