Node.js API compatibility - Timers
The timers module in Node.js provides a set of functions for scheduling the execution of code after a specified delay or at regular intervals. It’s essential for managing asynchronous operations and controlling the timing of function execution within applications. This module is available in the Azion Runtime through Node.js compatibility, and it’s commonly used in functions to defer work, add small delays, or coordinate the timing of asynchronous tasks with setTimeout.
Example: Basic setTimeout
The example below shows how to use the timers module in a function:
/** * 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;Example: Delayed execution with setTimeout
Schedule operations to run after a delay:
import timers from "node:timers";import { promisify } from "node:util";
// Promisify setTimeout for async/await usageconst 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;Example: setInterval for periodic operations
Execute code at regular intervals:
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;Example: Immediate execution with setImmediate
Execute code immediately after the current event loop:
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;Example: Timeout with cancellation
Implement cancellable timeouts for API calls:
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;Example: Timer promises (timers/promises)
Use the modern promise-based timer API:
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;Example: Retry logic with exponential backoff
Implement retry patterns using timers:
import timers from "node:timers";import { promisify } from "node:util";
const setTimeoutAsync = promisify(timers.setTimeout);
// Retry with exponential backoffconst 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;Supported APIs
| API | Status |
|---|---|
setTimeout() | 🟢 Supported |
clearTimeout() | 🟢 Supported |
setInterval() | 🟢 Supported |
clearInterval() | 🟢 Supported |
setImmediate() | 🟢 Supported |
clearImmediate() | 🟢 Supported |
timers/promises.setTimeout() | 🟢 Supported |
timers/promises.setInterval() | 🟢 Supported |
timers/promises.setImmediate() | 🟢 Supported |
timers.active() | 🟡 Partially supported |
timers.enroll() | 🔴 Not supported |
timers.unenroll() | 🔴 Not supported |
timers.getActive() | 🟡 Partially supported |