javascript-pro
JavaScript Pro
Guidelines for modern JavaScript and async programming patterns.
Core Principles
- Async/await over promise chains - Cleaner, more readable code
- Functional patterns - Map, filter, reduce over imperative loops
- Error boundaries - Handle errors at appropriate levels
- Module-first - ES modules with clean exports
- Defensive coding - Nullish coalescing and optional chaining
Modern Syntax (ES2020+)
Nullish Coalescing and Optional Chaining
// Nullish coalescing (??) - only null/undefined
const port = config.port ?? 3000;
// Optional chaining (?.)
const city = user?.address?.city;
const result = obj.method?.();
// Combined pattern
const name = data?.user?.name ?? 'Anonymous';
Destructuring and Spread
// Nested destructuring with defaults
const { user: { name = 'Guest', role = 'viewer' } = {} } = response;
// Rest in destructuring
const { id, ...rest } = obj;
// Spread for immutable updates
const updated = { ...original, status: 'active' };
const combined = [...arr1, ...arr2];
Modern Array Methods
// Array.at() for negative indexing
const last = arr.at(-1);
// Object.fromEntries() for object creation
const obj = Object.fromEntries([['a', 1], ['b', 2]]);
// Array.prototype.flatMap()
const words = sentences.flatMap(s => s.split(' '));
// Object.hasOwn() (safer than hasOwnProperty)
if (Object.hasOwn(obj, 'key')) { }
Async Patterns
Promise Combinators
// Promise.all - fail fast, all must succeed
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);
// Promise.allSettled - get all results regardless of failures
const results = await Promise.allSettled([
riskyOperation1(),
riskyOperation2()
]);
const successes = results.filter(r => r.status === 'fulfilled');
// Promise.race - first to complete wins
const result = await Promise.race([
fetchData(),
timeout(5000)
]);
// Promise.any - first success wins (ignores rejections)
const fastest = await Promise.any([
fetchFromCDN1(),
fetchFromCDN2()
]);
Async Error Handling
// Wrapper for clean try/catch
async function safeAsync(promise) {
try {
const data = await promise;
return [data, null];
} catch (error) {
return [null, error];
}
}
// Usage
const [user, error] = await safeAsync(fetchUser(id));
if (error) {
console.error('Failed to fetch user:', error.message);
return;
}
Async Iteration
// for-await-of for async iterables
async function* paginate(url) {
let nextUrl = url;
while (nextUrl) {
const response = await fetch(nextUrl);
const data = await response.json();
yield data.items;
nextUrl = data.nextPage;
}
}
for await (const page of paginate('/api/items')) {
processItems(page);
}
Controlled Concurrency
async function processWithLimit(items, fn, limit = 5) {
const results = [];
const executing = new Set();
for (const item of items) {
const promise = fn(item).then(result => {
executing.delete(promise);
return result;
});
executing.add(promise);
results.push(promise);
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
Module Patterns
Clean Exports
// Named exports for utilities
export function formatDate(date) { }
export function parseDate(str) { }
// Default export for main class/function
export default class ApiClient { }
// Re-exports for barrel files
export { formatDate, parseDate } from './dates.js';
export { default as ApiClient } from './client.js';
Dynamic Imports
// Code splitting with dynamic imports
const module = await import('./heavy-module.js');
// Conditional loading
if (featureEnabled) {
const { Feature } = await import('./feature.js');
new Feature().init();
}
Node.js Patterns
File System (fs/promises)
import { readFile, writeFile, mkdir } from 'fs/promises';
import { existsSync } from 'fs';
async function ensureDir(path) {
if (!existsSync(path)) {
await mkdir(path, { recursive: true });
}
}
const content = await readFile('config.json', 'utf-8');
const config = JSON.parse(content);
Streams
import { createReadStream, createWriteStream } from 'fs';
import { pipeline } from 'stream/promises';
import { createGzip } from 'zlib';
await pipeline(
createReadStream('input.txt'),
createGzip(),
createWriteStream('output.txt.gz')
);
Testing Patterns
import { describe, it, expect, vi } from 'vitest';
describe('UserService', () => {
it('fetches user by id', async () => {
const mockFetch = vi.fn().mockResolvedValue({
json: () => Promise.resolve({ id: 1, name: 'Test' })
});
const user = await fetchUser(1, { fetch: mockFetch });
expect(user.name).toBe('Test');
expect(mockFetch).toHaveBeenCalledWith('/api/users/1');
});
});
Performance Tips
- Use
Map/Setfor frequent lookups (O(1) vs O(n)) - Avoid creating functions in loops
- Use
requestAnimationFramefor DOM updates - Debounce/throttle event handlers
- Prefer
for...ofoverforEachfor break/continue support
Common Anti-Patterns
- Using
==instead of=== - Not handling promise rejections
- Modifying objects during iteration
- Using
varinstead ofconst/let - Callback hell instead of async/await
- Not using optional chaining for nested access
More from arosenkranz/claude-code-config
homelab-helper
Expert guidance for homelab infrastructure, self-hosting, and Raspberry Pi optimization. Use when recommending self-hosted services, configuring Docker services, setting up reverse proxies, integrating Home Assistant, or troubleshooting homelab networking.
17continuous-learning-v2
Instinct-based learning system that observes sessions via hooks, creates atomic instincts with confidence scoring, and evolves them into skills/commands/agents.
6session-log
Document conversation accomplishments in Obsidian vault with frontmatter, code changes, learning notes, and next steps.
6evolve
Cluster related instincts into skills, commands, or agents
6reclaude
Refactor CLAUDE.md files to follow progressive disclosure principles. Use when a CLAUDE.md file is too long or needs organizational improvement.
5find-skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
5