/** * Parte 5 — Concorrência não é paralelismo. * * npx tsx src/11-concorrencia-paralelismo.ts * * A Parte 4 ganhou tempo com `Promise.all` porque o tempo era espera. Aqui o * tempo é cálculo: as mesmas quatro assinaturas são produzidas em sequência, * sob `Promise.all` e em quatro workers. Um relógio de 50 ms mede, em cada * versão, se a thread principal ficou livre para atender outra coisa. */ import { availableParallelism } from 'node:os'; import { Worker } from 'node:worker_threads'; import { calcularAssinatura, RODADAS } from './assinatura.worker.js'; import { cronometro } from './biblioteca.js'; const ids = [1, 2, 3, 4]; // --------------------------------------------------------------------------- // Instrumento: conta quantas vezes um intervalo de 50 ms conseguiu disparar // enquanto a tarefa rodava. Se a thread principal estiver ocupada calculando, // o callback do intervalo não tem como entrar na pilha. // --------------------------------------------------------------------------- async function medir(rotulo: string, tarefa: () => Promise): Promise { const marcar = cronometro(); let batidas = 0; const relogio = setInterval(() => { batidas += 1; }, 50); const assinaturas = await tarefa(); clearInterval(relogio); marcar(`${rotulo}: ${assinaturas.length} assinaturas — relógio bateu ${batidas} vez(es)`); return assinaturas; } // --------------------------------------------------------------------------- // 1. Sequencial: uma assinatura depois da outra, na thread principal. // --------------------------------------------------------------------------- async function emSequencia(): Promise { return ids.map((id) => calcularAssinatura(id, RODADAS)); } // --------------------------------------------------------------------------- // 2. Promise.all: concorrente na forma, sequencial na execução. O corpo de // uma função async roda de forma síncrona até o primeiro await — e aqui não // há nenhum. Cada chamada calcula inteira antes de devolver a promise. // --------------------------------------------------------------------------- async function assinaturaAsync(id: number): Promise { return calcularAssinatura(id, RODADAS); } async function comPromiseAll(): Promise { return Promise.all(ids.map((id) => assinaturaAsync(id))); } // --------------------------------------------------------------------------- // 3. Workers: cada assinatura em uma thread própria. A thread principal só // cria os workers e espera pelas mensagens — e esperar ela sabe fazer. // --------------------------------------------------------------------------- function assinaturaEmWorker(id: number): Promise { return new Promise((resolve, reject) => { // O caminho aponta para o arquivo `.ts` porque este projeto roda com // `tsx`. Em um projeto compilado, ele apontaria para o `.js` gerado. const worker = new Worker(new URL('./assinatura.worker.ts', import.meta.url), { workerData: { livroId: id, rodadas: RODADAS }, }); worker.once('message', (valor: unknown) => { if (typeof valor === 'number') resolve(valor); else reject(new TypeError('o worker devolveu um valor que não é número')); }); worker.once('error', reject); worker.once('exit', (codigo) => { if (codigo !== 0) reject(new Error(`worker terminou com código ${codigo}`)); }); }); } async function comWorkers(): Promise { return Promise.all(ids.map((id) => assinaturaEmWorker(id))); } async function principal(): Promise { console.log(`núcleos disponíveis: ${availableParallelism()}`); const a = await medir('sequencial ', emSequencia); const b = await medir('Promise.all', comPromiseAll); const c = await medir('4 workers ', comWorkers); const iguais = a.every((valor, i) => valor === b[i] && valor === c[i]); console.log(` mesmas assinaturas nas três versões: ${iguais}`); } void principal();