Execução Sequencial de Promises com Array.prototype.reduce no JavaScript
Executar chamadas assíncronas concorrentemente com Promise.all pode sobrecarregar servidores ou ultrapassar limites de taxa (rate limits) de APIs externas. Encadeiar operações assíncronas usando o método Array.prototype.reduce() resolve esse problema, garantindo que cada Promise seja iniciada apenas após a conclusão bem-sucedida da anterior.
JavaScript
/**
* Executa uma lista de tarefas assíncronas em sequência.
* @param {Array<() => Promise<any>>} tarefas - Array de funções que retornam Promises.
* @returns {Promise<Array<any>>} Promessa com o array de resultados em ordem.
*/
async function executarEmFila(tarefas) {
// O acumulador inicia com uma Promise resolvida contendo um array vazio
return tarefas.reduce(async (promessaAnterior, criarTarefa) => {
const resultados = await promessaAnterior;
const novoResultado = await criarTarefa();
return [...resultados, novoResultado];
}, Promise.resolve([]));
}
// --- Exemplo Prático de Uso ---
// Função simuladora de requisição assíncrona (ex: chamada de API)
const criarRequisicao = (id, ms) => () =>
new Promise((resolve) => {
setTimeout(() => {
console.log(`[${new Date().toLocaleTimeString()}] Requisição ${id} concluída`);
resolve(`Dados_${id}`);
}, ms);
});
// Fila de funções geradoras de Promises
const filaDeTarefas = [
criarRequisicao(1, 1000), // Demora 1s
criarRequisicao(2, 500), // Demora 0.5s (aguarda a #1 finalizar)
criarRequisicao(3, 800) // Demora 0.8s (aguarda a #2 finalizar)
];
console.log('Iniciando processamento em fila...');
executarEmFila(filaDeTarefas).then((resultados) => {
console.log('Todos os resultados recebidos:', resultados);
});
Pontos-chave
- Funções Geradoras de Promises (Thunks): O array deve conter funções que retornam Promises, e não as Promises já instanciadas; caso contrário, as requisições dispararão em paralelo no momento da criação do array.
- Início com
Promise.resolve([]): Definir uma Promise resolvida contendo um array vazio como valor inicial doreduceestabelece o ponto de partida do encadeamento sem bloqueios. - Controle de Tratamento de Erros: Se uma Promise na fila for rejeitada, a execução sequencial será interrompida imediatamente, a menos que haja um bloco
try/catchinterno na função de callback da tarefa. - Alternativa Moderna com
for...of: Para cenários mais simples sem acúmulo de estado noreduce, um loopfor (const tarefa of tarefas)combinado comawaitoferece sintaxe equivalente com legibilidade direta.
