Timers & Async Operations Guide
Learn how to use timers and asynchronous operations in your Invoke functions.
setTimeout
Execute code after a delay:
module.exports = function(req, res) {
console.log('Start');
setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
res.send('Timer set');
};
With Async/Await
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = async function(req, res) {
console.log('Start');
await delay(2000);
console.log('After 2 seconds');
res.send('Done');
};
setInterval
Execute code repeatedly at intervals:
module.exports = function(req, res) {
let count = 0;
const interval = setInterval(() => {
count++;
console.log('Count:', count);
if (count >= 5) {
clearInterval(interval);
res.send('Completed 5 iterations');
}
}, 1000);
};
setImmediate
Execute on next event loop tick:
module.exports = function(req, res) {
console.log('1');
setImmediate(() => {
console.log('3 - Immediate');
});
console.log('2');
// Output: 1, 2, 3 - Immediate
res.send('Done');
};
sleep() - Invoke-Specific
Promise-based sleep function:
module.exports = async function(req, res) {
console.log('Start:', new Date().toISOString());
await sleep(1000);
console.log('After 1 second');
await sleep(2000);
console.log('After 3 seconds total');
res.json({
message: 'Completed',
timestamp: new Date().toISOString()
});
};
Timers/Promises API
Modern promise-based timers:
const { setTimeout, setInterval } = require('timers/promises');
module.exports = async function(req, res) {
// Promise-based setTimeout
await setTimeout(1000);
console.log('After 1 second');
// With value
const result = await setTimeout(1000, 'delayed value');
console.log(result); // 'delayed value'
res.send('Done');
};
Async Interval
const { setInterval } = require('timers/promises');
module.exports = async function(req, res) {
const messages = [];
let count = 0;
for await (const startTime of setInterval(1000, Date.now())) {
messages.push(`Tick ${++count} at ${new Date().toISOString()}`);
if (count >= 5) {
break;
}
}
res.json({ messages });
};
AbortController with Timers
Cancel timers using AbortController:
const { setTimeout } = require('timers/promises');
module.exports = async function(req, res) {
const controller = new AbortController();
// Cancel after 3 seconds
setTimeout(3000).then(() => controller.abort());
try {
await setTimeout(10000, 'completed', {
signal: controller.signal
});
res.send('Completed 10 seconds');
} catch (error) {
if (error.name === 'AbortError') {
res.send('Cancelled after 3 seconds');
} else {
throw error;
}
}
};
Common Patterns
Retry with Exponential Backoff
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
if (response.ok) return await response.json();
if (i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000;
console.log(`Retry ${i + 1} after ${delay}ms`);
await sleep(delay);
}
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(Math.pow(2, i) * 1000);
}
}
}
module.exports = async function(req, res) {
const data = await fetchWithRetry('https://api.example.com/data');
res.json(data);
};
Timeout Wrapper
async function withTimeout(promise, ms) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), ms);
try {
const result = await promise;
clearTimeout(timeout);
return result;
} catch (error) {
clearTimeout(timeout);
throw error;
}
}
module.exports = async function(req, res) {
try {
const data = await withTimeout(
fetch('https://api.example.com/slow').then(r => r.json()),
5000
);
res.json(data);
} catch (error) {
res.status(408).json({ error: 'Request timeout' });
}
};
Debounce
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
module.exports = async function(req, res) {
const processRequest = debounce(async (data) => {
console.log('Processing:', data);
await kv.set('last:request', data);
}, 1000);
processRequest(req.body);
res.send('Request queued');
};
Throttle
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
module.exports = function(req, res) {
const logRequest = throttle(() => {
console.log('Request logged at', new Date().toISOString());
}, 5000);
logRequest();
res.send('OK');
};
Polling
async function poll(fn, validate, interval = 1000, maxAttempts = 30) {
for (let i = 0; i < maxAttempts; i++) {
const result = await fn();
if (validate(result)) {
return result;
}
await sleep(interval);
}
throw new Error('Max polling attempts exceeded');
}
module.exports = async function(req, res) {
try {
const result = await poll(
() => fetch('https://api.example.com/job/123').then(r => r.json()),
(data) => data.status === 'completed',
2000, // Check every 2 seconds
15 // Max 30 seconds
);
res.json(result);
} catch (error) {
res.status(408).json({ error: 'Job did not complete in time' });
}
};
Rate Limiting with Timers
module.exports = async function(req, res) {
const clientId = req.headers['x-client-id'] || 'anonymous';
const key = `ratelimit:${clientId}`;
const requests = await kv.get(key) || [];
const now = Date.now();
const oneMinuteAgo = now - 60000;
// Remove old requests
const recentRequests = requests.filter(time => time > oneMinuteAgo);
if (recentRequests.length >= 10) {
const oldestRequest = recentRequests[0];
const resetTime = new Date(oldestRequest + 60000).toISOString();
return res.status(429).json({
error: 'Rate limit exceeded',
resetAt: resetTime
});
}
// Add current request
recentRequests.push(now);
await kv.set(key, recentRequests, 60000);
res.json({ success: true });
};
Best Practices
1. Clean Up Timers
// ✅ Always clear timers
const timeout = setTimeout(() => {}, 5000);
clearTimeout(timeout);
const interval = setInterval(() => {}, 1000);
clearInterval(interval);
2. Use sleep() for Simple Delays
// ❌ Verbose
await new Promise(resolve => setTimeout(resolve, 1000));
// ✅ Simple
await sleep(1000);
3. Handle Long-Running Operations
// Set reasonable timeouts
const controller = new AbortController();
setTimeout(() => controller.abort(), 30000);
await fetch(url, { signal: controller.signal });
4. Avoid Blocking
// ❌ Blocking (if possible)
for (let i = 0; i < 1000000; i++) { /* heavy work */ }
// ✅ Non-blocking
for (let i = 0; i < 1000; i++) {
// Do work in chunks
if (i % 100 === 0) await sleep(0); // Yield to event loop
}
Next Steps
- Timers Module - Complete API reference
- HTTP Requests - Async request patterns
- Examples - Async function examples