# Compatibilidade APIs Node.js - Events

A API `events` no Node.js é um módulo core que fornece uma maneira de trabalhar com programação orientada a eventos. Ela permite que desenvolvedores criem e gerenciem event emitters, que são objetos que podem emitir eventos nomeados e ouvir esses eventos através de event listeners. Este módulo está disponível no Azion Runtime através da compatibilidade com Node.js, sendo útil para desacoplar lógica em functions através da emissão de eventos e reação a eles com listeners dedicados.

---

## Exemplo: Emissão básica de eventos

O exemplo abaixo mostra como usar o módulo `events` em uma function:

```javascript
/**
 * An example of using the Node.js `events` module in Azion Functions.
 * Support:
 * - Partial support
 * - Extended by library `events`
 * @example
 * // Execute with Azion Bundler:
 * npx edge-functions build
 * npx edge-functions dev
 */
import { EventEmitter } from "node:events";

/**
 * Emit an event and listen to it.
 * @param {*} event
 * @returns {Response} Response
 */
const main = async (event) => {
  const emitter = new EventEmitter();

  emitter.on("hello-event", (...args) => {
    console.log("an event occurred!", ...args);
  });

  emitter.emit("hello-event", 1, 2, 3);
  return new Response("Event emitted", { status: 200 });
};

export default main;
```

---

## Exemplo: Eventos de ciclo de vida de requisição

Use EventEmitter para lidar com estágios de processamento de requisições:

```javascript
import { EventEmitter } from "node:events";

const main = async (event) => {
  const requestEmitter = new EventEmitter();
  const results = [];

  // Get request info with optional chaining for safety
  const requestUrl = event.request?.url || "https://default.example.com";
  const requestHeaders = event.request?.headers || {};

  // Listen for request start
  requestEmitter.on("start", (url) => {
    console.log(`Processing request: ${url}`);
    results.push(`Started: ${url}`);
  });

  // Listen for validation events
  requestEmitter.on("validate", (data) => {
    if (data.headers) {
      console.log("Headers validated");
      results.push("Headers validated");
    }
  });

  // Listen for completion
  requestEmitter.on("complete", (status) => {
    console.log(`Request completed with status: ${status}`);
    results.push(`Completed: ${status}`);
  });

  // Listen for errors
  requestEmitter.on("error", (err) => {
    console.error(`Error: ${err.message}`);
    results.push(`Error: ${err.message}`);
  });

  // Emit events in sequence
  requestEmitter.emit("start", requestUrl);
  requestEmitter.emit("validate", { headers: requestHeaders });
  requestEmitter.emit("complete", 200);

  return new Response(JSON.stringify({ events: results }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Exemplo: Listeners de única execução e tratamento de erros

Use `once()` para listeners de execução única e trate erros adequadamente:

```javascript
import { EventEmitter } from "node:events";

const main = async (event) => {
  const emitter = new EventEmitter();
  const log = [];

  // One-time listener - only executes once
  emitter.once("init", () => {
    console.log("Initialization complete");
    log.push("Initialized");
  });

  // Multiple listeners for same event
  emitter.on("data", (chunk) => {
    console.log(`Processing chunk: ${chunk}`);
    log.push(`Chunk: ${chunk}`);
  });

  emitter.on("data", (chunk) => {
    console.log(`Logging chunk: ${chunk}`);
    log.push(`Logged: ${chunk}`);
  });

  // Error handling - special 'error' event
  emitter.on("error", (err) => {
    console.error(`Error caught: ${err.message}`);
    log.push(`Error: ${err.message}`);
  });

  // Emit events
  emitter.emit("init");
  emitter.emit("init"); // Won't trigger again - once() used
  emitter.emit("data", "chunk-1");
  emitter.emit("data", "chunk-2");
  emitter.emit("error", new Error("Test error"));

  return new Response(JSON.stringify({ log }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Exemplo: Handlers de eventos assíncronos

Manipule operações assíncronas em event listeners:

```javascript
import { EventEmitter } from "node:events";
import { setTimeout } from "node:timers/promises";

const main = async (event) => {
  const emitter = new EventEmitter();
  const results = [];

  // Note: EventEmitter does NOT await Promises returned by listeners.
  // The emit() method returns immediately, without waiting for async operations.
  // For async operations, it's recommended to use Promise-based patterns
  // instead of relying on EventEmitter alone.

  // Listen for fetch completion events
  emitter.on("fetched", (result) => {
    results.push(result);
    console.log(`Fetched: ${result.url}`);
  });

  // Error handler
  emitter.on("error", (err) => {
    console.error(`Fetch error: ${err.message}`);
    results.push({ error: err.message });
  });

  // Define async fetch function that emits events
  const fetchData = async (url) => {
    try {
      const response = await fetch(url);
      const data = await response.json();
      const result = { url, status: response.status, data };
      emitter.emit("fetched", result);
      return result;
    } catch (error) {
      emitter.emit("error", error);
      return { url, error: error.message };
    }
  };

  // URLs to fetch
  const urls = [
    "https://jsonplaceholder.typicode.com/todos/1",
    "https://jsonplaceholder.typicode.com/todos/2"
  ];

  // Execute fetches in parallel and wait for completion
  const fetchResults = await Promise.all(urls.map(fetchData));

  return new Response(JSON.stringify({ 
    results,
    fetchResults 
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Exemplo: Classe EventEmitter personalizada

Estenda EventEmitter para criar event emitters específicos de domínio:

```javascript
import { EventEmitter } from "node:events";

// Custom class extending EventEmitter
class RequestHandler extends EventEmitter {
  constructor() {
    super();
    this.requestCount = 0;
  }

  processRequest(request) {
    this.requestCount++;
    this.emit("request", { id: this.requestCount, url: request.url });

    try {
      // Simulate processing
      const result = { processed: true, id: this.requestCount };
      this.emit("success", result);
      return result;
    } catch (error) {
      this.emit("error", error);
      throw error;
    }
  }
}

const main = async (event) => {
  const handler = new RequestHandler();
  const logs = [];

  // Attach listeners
  handler.on("request", (data) => {
    logs.push(`Request #${data.id}: ${data.url}`);
  });

  handler.on("success", (result) => {
    logs.push(`Success: ${JSON.stringify(result)}`);
  });

  handler.on("error", (err) => {
    logs.push(`Error: ${err.message}`);
  });

  // Process requests
  handler.processRequest(event.request);
  handler.processRequest({ url: "https://example.com/test" });

  // Get listener counts
  const listenerCount = handler.listenerCount("request");
  logs.push(`Request listeners: ${listenerCount}`);

  return new Response(JSON.stringify({ logs, totalRequests: handler.requestCount }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## APIs com suporte

| API | Status |
|-----|--------|
| `new EventEmitter()` | 🟢 Com suporte |
| `emitter.on()` | 🟢 Com suporte |
| `emitter.once()` | 🟢 Com suporte |
| `emitter.emit()` | 🟢 Com suporte |
| `emitter.off()` | 🟢 Com suporte |
| `emitter.removeListener()` | 🟢 Com suporte |
| `emitter.removeAllListeners()` | 🟢 Com suporte |
| `emitter.listenerCount()` | 🟢 Com suporte |
| `emitter.listeners()` | 🟢 Com suporte |
| `emitter.eventNames()` | 🟢 Com suporte |
| `emitter.prependListener()` | 🟢 Com suporte |
| `emitter.prependOnceListener()` | 🟢 Com suporte |
| `EventEmitter.listenerCount()` | 🟢 Com suporte |
| `EventEmitter.defaultMaxListeners` | 🟢 Com suporte |
| `captureRejections` | 🟡 Parcialmente suportado |

:::note
APIs marcadas como 🟡 Parcialmente suportado têm funcionalidade limitada em comparação com a implementação completa do Node.js. A opção `captureRejections` tem suporte parcial — é recomendado tratar Promise rejections explicitamente em event listeners assíncronos em vez de depender da captura automática.
:::

---

## Recursos relacionados

- [Documentação do `events` no Node.js](https://nodejs.org/api/events.html)