# Compatibilidade APIs Node.js - Timers

O módulo `timers` no Node.js fornece um conjunto de funções para agendar a execução de código após um atraso especificado ou em intervalos regulares. É essencial para gerenciar operações assíncronas e controlar o tempo de execução de funções dentro de aplicações. Este módulo está disponível no Azion Runtime através da compatibilidade com Node.js, sendo comumente usado em functions para adiar trabalho, adicionar pequenos atrasos ou coordenar o tempo de tarefas assíncronas com `setTimeout`.

---

## Exemplo: setTimeout básico

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

```javascript
/**
 * An example of using Node.js Timers API in an Azion Function.
 * Support:
 * - Partially supported (Extended by library `timers-browserify`)
 * @module runtime-apis/nodejs/timers/main
 * @example
 * // Execute with Azion Bundler:
 * npx edge-functions build
 *
 */
import timers from "node:timers";

/**
 * An example of using the Node.js Timers API in an Azion Function.
 * @param {*} event
 * @returns {Promise<Response>}
 */
const main = async (event) => {
  console.log("Hello, world!");
  console.log("Waiting for 5 seconds...");
  await new Promise((resolve) => timers.setTimeout(resolve, 2000));
  return new Response("Done!", { status: 200 });
};

export default main;
```

---

## Exemplo: Execução adiada com setTimeout

Agende operações para executar após um atraso:

```javascript
import timers from "node:timers";
import { promisify } from "node:util";

// Promisify setTimeout for async/await usage
const setTimeoutAsync = promisify(timers.setTimeout);

const main = async (event) => {
  const startTime = Date.now();
  const logs = [];

  logs.push(`Start: ${startTime}`);

  // Using promisified version - properly awaited
  await setTimeoutAsync(100);
  logs.push(`After 100ms delay`);

  // Multiple sequential delays
  await setTimeoutAsync(100);
  logs.push(`After additional 100ms`);

  // Calculate total time
  const totalTime = Date.now() - startTime;

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

export default main;
```

---

## Exemplo: setInterval para operações periódicas

Execute código em intervalos regulares:

```javascript
import timers from "node:timers";

const main = async (event) => {
  const startTime = Date.now();
  const ticks = [];
  let tickCount = 0;

  // Create interval that runs every 50ms
  const intervalId = timers.setInterval(() => {
    tickCount++;
    const elapsed = Date.now() - startTime;
    ticks.push({ tick: tickCount, elapsed });
    console.log(`Tick ${tickCount} at ${elapsed}ms`);
  }, 50);

  // Run for 250ms then stop
  await new Promise(resolve => timers.setTimeout(resolve, 250));

  // Clear the interval
  timers.clearInterval(intervalId);

  console.log(`Total ticks: ${tickCount}`);

  return new Response(JSON.stringify({
    totalTicks: tickCount,
    ticks,
    durationMs: Date.now() - startTime
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Exemplo: Execução imediata com setImmediate

Execute código imediatamente após o event loop atual:

```javascript
import timers from "node:timers";

const main = async (event) => {
  const executionOrder = [];

  executionOrder.push("1. Start of function");

  // setImmediate runs after current code completes
  timers.setImmediate(() => {
    executionOrder.push("setImmediate callback");
  });

  // setTimeout with 0 delay still goes through timer phase
  timers.setTimeout(() => {
    executionOrder.push("setTimeout(0) callback");
  }, 0);

  // Process.nextTick runs before setImmediate (if available)
  if (typeof process?.nextTick === "function") {
    process.nextTick(() => {
      executionOrder.push("nextTick callback");
    });
  }

  executionOrder.push("2. End of synchronous code");

  // Wait for all callbacks to execute
  await new Promise(resolve => timers.setTimeout(resolve, 50));

  executionOrder.push("3. After waiting");

  // Note: The execution order between setImmediate and setTimeout(0)
  // may vary across environments and is not guaranteed.
  // In edge runtimes, this order can differ from Node.js.

  return new Response(JSON.stringify({
    executionOrder,
    note: "Order between setImmediate and setTimeout(0) may vary"
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Exemplo: Timeout com cancelamento

Implemente timeouts canceláveis para chamadas de API:

```javascript
import timers from "node:timers";

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

  // Create a timeout that can be cancelled
  const timeoutId = timers.setTimeout(() => {
    results.push("Timeout executed");
  }, 500);

  // Cancel the timeout before it fires
  timers.setTimeout(() => {
    results.push("Cancelling timeout");
    timers.clearTimeout(timeoutId);
  }, 200);

  // Wait to see what happens
  await new Promise(resolve => timers.setTimeout(resolve, 600));

  // Timeout with race pattern for API calls
  const fetchWithTimeout = async (url, timeoutMs) => {
    const controller = new AbortController();
    const timeout = timers.setTimeout(() => controller.abort(), timeoutMs);

    try {
      const response = await fetch(url, { signal: controller.signal });
      timers.clearTimeout(timeout);
      return { success: true, status: response.status };
    } catch (error) {
      timers.clearTimeout(timeout);
      return { success: false, error: error.message };
    }
  };

  // Test with a fast-responding API
  const apiResult = await fetchWithTimeout(
    "https://jsonplaceholder.typicode.com/todos/1",
    5000
  );
  results.push(`API result: ${JSON.stringify(apiResult)}`);

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

export default main;
```

---

## Exemplo: Promises de timer (timers/promises)

Use a API moderna de timer baseada em promises:

```javascript
import { setTimeout, setInterval, setImmediate } from "node:timers/promises";

const main = async (event) => {
  const startTime = Date.now();
  const logs = [];

  logs.push(`Start: ${startTime}`);

  // Promise-based setTimeout
  await setTimeout(100);
  logs.push(`After 100ms: ${Date.now() - startTime}ms elapsed`);

  // setTimeout with value
  const result = await setTimeout(50, "timer result");
  logs.push(`Got value: ${result}`);

  // Async iterator for setInterval with proper cancellation
  // Note: Use AbortController to prevent memory leaks
  let count = 0;
  const ac = new AbortController();
  const interval = setInterval(30, undefined, { signal: ac.signal });

  try {
    for await (const _ of interval) {
      count++;
      logs.push(`Interval tick ${count}`);
      if (count >= 3) {
        ac.abort(); // Properly cancel the interval
        break;
      }
    }
  } catch (error) {
    // AbortError is expected when we cancel
    if (error.name !== "AbortError") {
      throw error;
    }
  }

  // setImmediate as promise
  await setImmediate();
  logs.push("After setImmediate");

  // Calculate total time
  const totalTime = Date.now() - startTime;

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

export default main;
```

---

## Exemplo: Lógica de retry com backoff exponencial

Implemente padrões de retry usando timers:

```javascript
import timers from "node:timers";
import { promisify } from "node:util";

const setTimeoutAsync = promisify(timers.setTimeout);

// Retry with exponential backoff
const retryWithBackoff = async (fn, maxRetries, initialDelay = 100) => {
  let lastError;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const result = await fn();
      return { success: true, result, attempts: attempt + 1 };
    } catch (error) {
      lastError = error;
      
      if (attempt < maxRetries - 1) {
        const delay = initialDelay * Math.pow(2, attempt);
        console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms`);
        await setTimeoutAsync(delay);
      }
    }
  }
  
  return { success: false, error: lastError.message, attempts: maxRetries };
};

const main = async (event) => {
  // Simulate a flaky operation
  let attempts = 0;
  const flakyOperation = async () => {
    attempts++;
    if (attempts < 3) {
      throw new Error(`Attempt ${attempts} failed`);
    }
    return `Success on attempt ${attempts}`;
  };

  const result = await retryWithBackoff(flakyOperation, 5, 50);

  // Simulate a timeout scenario
  const timeoutOperation = async () => {
    await setTimeoutAsync(200);
    return "This should not appear";
  };

  const timeoutResult = await Promise.race([
    timeoutOperation(),
    setTimeoutAsync(50).then(() => ({ timedOut: true }))
  ]);

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

export default main;
```

---

## APIs com suporte

| API | Status |
|-----|--------|
| `setTimeout()` | 🟢 Com suporte |
| `clearTimeout()` | 🟢 Com suporte |
| `setInterval()` | 🟢 Com suporte |
| `clearInterval()` | 🟢 Com suporte |
| `setImmediate()` | 🟢 Com suporte |
| `clearImmediate()` | 🟢 Com suporte |
| `timers/promises.setTimeout()` | 🟢 Com suporte |
| `timers/promises.setInterval()` | 🟢 Com suporte |
| `timers/promises.setImmediate()` | 🟢 Com suporte |
| `timers.active()` | 🟡 Parcialmente suportado |
| `timers.enroll()` | 🔴 Sem suporte |
| `timers.unenroll()` | 🔴 Sem suporte |
| `timers.getActive()` | 🟡 Parcialmente suportado |

:::note
APIs marcadas como 🟡 Parcialmente suportado têm funcionalidade limitada em comparação com a implementação completa do Node.js. As APIs `enroll()` e `unenroll()` estão depreciadas no Node.js e não têm suporte no Azion Runtime.
:::

---

## Recursos relacionados

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