<h2>O Que é uma Promise e Por Que Implementar do Zero</h2>
<p>Uma Promise é um objeto JavaScript que representa a eventual conclusão (ou falha) de uma operação assíncrona e seu valor resultante. Aprender a implementar uma Promise internamente é fundamental para entender como JavaScript gerencia operações assíncronas. Quando você implementa do zero, compreende os estados (pending, fulfilled, rejected), o fluxo de callbacks e por que o padrão é tão poderoso para evitar callback hell.</p>
<p>Nesta aula, construiremos uma Promise funcional, respeitando a especificação Promises/A+. Você verá que uma Promise não é "mágica" — é um padrão bem estruturado que você pode reproduzir. Após esta jornada, entenderá por que <code>.then()</code>, <code>.catch()</code> e <code>.finally()</code> funcionam como funcionam, e poderá até debugar Promises com confiança.</p>
<h2>Arquitetura Interna: Estados e Transições</h2>
<h3>Os Três Estados de uma Promise</h3>
<p>Uma Promise começa em estado <strong>pending</strong> (pendente). Durante a execução do executor (a função passada ao construtor), ela pode fazer uma transição irreversível para <strong>fulfilled</strong> (cumprida, com um valor) ou <strong>rejected</strong> (rejeitada, com uma razão/erro).</p>
<pre><code class="language-javascript">class MinhaPromise {
constructor(executor) {
this.state = 'pending'; // pending | fulfilled | rejected
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(cb => cb(value));
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb(reason));
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
// Uso
new MinhaPromise((resolve, reject) => {
setTimeout(() => resolve('Sucesso!'), 1000);
});</code></pre>
<p>O executor é chamado <strong>imediatamente</strong> com as funções <code>resolve</code> e <code>reject</code>. A transição de estado só ocorre uma vez — chamadas subsequentes são ignoradas. Os callbacks são armazenados para execução quando a Promise se resolver.</p>
<h2>Implementando .then(), .catch() e Encadeamento</h2>
<h3>O Método then() e Chain-ability</h3>
<p>O <code>.then()</code> retorna uma <strong>nova Promise</strong>, não a original. Isso permite encadeamento. A novidade aqui é a resolução de Promises intermediárias — se um callback retorna outra Promise, a Promise filha aguarda sua conclusão.</p>
<pre><code class="language-javascript">class MinhaPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(cb => cb());
};
const reject = (reason) => {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb());
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// Garantir que são funções
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
return new MinhaPromise((resolve, reject) => {
const handleFulfilled = () => {
try {
const result = onFulfilled(this.value);
resolvePromise(this, result, resolve, reject);
} catch (error) {
reject(error);
}
};
const handleRejected = () => {
try {
const result = onRejected(this.reason);
resolvePromise(this, result, resolve, reject);
} catch (error) {
reject(error);
}
};
if (this.state === 'fulfilled') {
setTimeout(handleFulfilled, 0);
} else if (this.state === 'rejected') {
setTimeout(handleRejected, 0);
} else {
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(
value => MinhaPromise.resolve(onFinally()).then(() => value),
reason => MinhaPromise.resolve(onFinally()).then(() => { throw reason; })
);
}
static resolve(value) {
if (value instanceof MinhaPromise) return value;
return new MinhaPromise(resolve => resolve(value));
}
static reject(reason) {
return new MinhaPromise((_, reject) => reject(reason));
}
}
function resolvePromise(promise, x, resolve, reject) {
if (x === promise) {
reject(new TypeError('Circular reference'));
return;
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(
x,
y => resolvePromise(promise, y, resolve, reject),
r => reject(r)
);
return;
}
} catch (error) {
reject(error);
return;
}
}
resolve(x);
}
// Teste
new MinhaPromise((resolve) => {
resolve(5);
})
.then(value => {
console.log(value); // 5
return value * 2;
})
.then(value => {
console.log(value); // 10
return new MinhaPromise(res => res(value + 10));
})
.then(value => console.log(value)) // 20
.catch(err => console.error(err));</code></pre>
<h3>Mecanismo de Resolução (Thenable)</h3>
<p>A função <code>resolvePromise</code> é crítica: ela detecta quando um callback retorna um objeto "thenable" (que possui <code>.then()</code>) e aguarda sua resolução. Isso permite composição de Promises e é o coração do encadeamento. A função usa <code>setTimeout</code> para garantir execução assíncrona, evitando bloqueios.</p>
<h2>Tratamento de Erros e Casos Especiais</h2>
<h3>Captura de Exceções e Propagação</h3>
<p>Erros lançados em executores são automaticamente convertidos em rejeições. Quando um callback lança erro, a Promise resultante é rejeitada. O <code>.catch()</code> é apenas sintaxe para <code>.then(null, handler)</code>.</p>
<pre><code class="language-javascript">// Erro no executor
new MinhaPromise((resolve, reject) => {
throw new Error('Boom!');
})
.catch(err => console.error('Capturado:', err.message));
// Erro em callback
MinhaPromise.resolve(10)
.then(value => {
throw new Error('Erro no then');
})
.catch(err => console.log('Tratado:', err.message));
// Propagação de erro
MinhaPromise.reject('Motivo')
.then(x => x * 2) // Pulado
.then(x => x + 5) // Pulado
.catch(reason => console.log('Final:', reason)); // 'Motivo'</code></pre>
<p>A beleza desta arquitetura é que erros propagam automaticamente pela chain até encontrar um <code>.catch()</code>. Sem tratamento, o erro é "silenciosamente perdido" em Promises nativas — implementações reais têm mecanismos para avisar sobre rejeições não tratadas.</p>
<h2>Conclusão</h2>
<p><strong>Três aprendizados principais:</strong> (1) Uma Promise é um gerenciador de estado que transiciona entre pendente, cumprido e rejeitado, executando callbacks registrados no tempo certo; (2) O <code>.then()</code> retorna sempre uma nova Promise, permitindo encadeamento transparente através do mecanismo de resolução de Promises intermediárias; (3) Erros são parte integral do fluxo — tratados como rejeições que propagam pela corrente até serem capturados ou descartados.</p>
<p>Implementar do zero revela que Promises não contêm "magia assíncrona" — elas organizam callbacks de forma previsível. Agora você pode ler código assíncrono com confiança, entender freezes e debugar problemas de timing. Na próxima vez que usar <code>async/await</code> (que é açúcar sintático sobre Promises), saberá exatamente o que está acontecendo internamente.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://promisesaplus.com/" target="_blank" rel="noopener noreferrer">Promises/A+ Specification</a> — Especificação oficial que padroniza o comportamento de Promises</li>
<li><a href="https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise" target="_blank" rel="noopener noreferrer">MDN Web Docs - Promise</a> — Documentação oficial da Mozilla</li>
<li><a href="https://github.com/getify/You-Dont-Know-JS/tree/2nd-ed/async-performance" target="_blank" rel="noopener noreferrer">You Don't Know JS: Async & Performance</a> — Livro de Kyle Simpson sobre assincronismo</li>
<li><a href="https://javascript.info/promise-basics" target="_blank" rel="noopener noreferrer">JavaScript.info - Promise</a> — Tutorial interativo com explicações profundas</li>
<li><a href="https://exploringjs.com/es6/ch_promises.html" target="_blank" rel="noopener noreferrer">Exploring ES6 - Promises</a> — Capítulo do livro de Axel Rauschmayer sobre ES6</li>
</ul>