asyncState.error
Property that holds error information when async operations fail.
Quick Start (30 seconds)
const state = asyncState(null);
console.log(state.error); // null
await execute(state, async () => {
throw new Error('Something went wrong!');
});
console.log(state.error); // Error: Something went wrong!
console.log(state.error.message); // 'Something went wrong!'
// Clear error
reset(state);
console.log(state.error); // nullThe magic: error automatically captures exceptions—null when OK, Error object when failed!
What is asyncState.error?
error is a reactive property that stores error objects when async operations fail. It's automatically managed by execute().
Key points:
- Read/write property
- Starts as
null(no error) - Set to Error object when
execute()throws - Cleared to
nullwhen newexecute()starts - Remains after error until cleared
- Reactive—watchers and effects track it
Why Does This Exist?
Without Error Property
// Manual error management
let errorMessage = null;
async function fetchData() {
try {
const response = await fetch('/api/data');
return response.json();
} catch (e) {
errorMessage = e.message; // Manually track
}
}Problems: Must manually catch/clear, no automatic reactivity, separate from state.
With asyncState
const state = asyncState(null);
await execute(state, async () => {
const response = await fetch('/api/data');
return response.json();
});
if (state.error) {
console.error('Failed:', state.error.message);
}Benefits: Automatic error capture, centralized with other state, reactive.
Basic Usage
Example 1: Error Handling
const state = asyncState(null);
await execute(state, async () => {
const response = await fetch('/api/nonexistent');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
});
if (state.error) {
console.error('Error occurred:', state.error.message);
console.error('Stack:', state.error.stack);
}Example 2: Display Error Message
const state = asyncState(null);
function render() {
if (state.loading) {
return 'Loading...';
}
if (state.error) {
return `
<div class="error">
<h3>Error</h3>
<p>${state.error.message}</p>
<button onclick="retry()">Retry</button>
</div>
`;
}
if (state.data) {
return `<div>Data: ${state.data}</div>`;
}
return '<button>Load</button>';
}Example 3: Error Cleared on Retry
const state = asyncState(null);
// First attempt fails
await execute(state, async () => {
throw new Error('Failed!');
});
console.log(state.error); // Error: Failed!
// Retry clears error automatically
await execute(state, async () => {
return 'success';
});
console.log(state.error); // null (cleared)
console.log(state.data); // 'success'Common Patterns
Pattern 1: Error Notification
const state = asyncState(null);
watch(state, {
error: (newError, oldError) => {
if (newError) {
showNotification({
type: 'error',
message: newError.message,
duration: 5000
});
}
}
});
await execute(state, async () => {
throw new Error('Network timeout');
});
// Shows notification automaticallyPattern 2: Error Types
const state = asyncState(null);
await execute(state, async () => {
const response = await fetch('/api/data');
if (!response.ok) {
const error = new Error(`HTTP ${response.status}`);
error.status = response.status;
error.type = 'http';
throw error;
}
return response.json();
});
if (state.error) {
switch (state.error.type) {
case 'http':
console.error('HTTP error:', state.error.status);
break;
default:
console.error('Unknown error:', state.error.message);
}
}Pattern 3: Retry with Error Check
const state = asyncState(null);
async function fetchWithRetry(maxRetries = 3) {
let attempts = 0;
while (attempts < maxRetries) {
await execute(state, async () => {
const response = await fetch('/api/data');
return response.json();
});
if (!state.error) {
return state.data; // Success
}
attempts++;
console.log(`Attempt ${attempts} failed:`, state.error.message);
if (attempts < maxRetries) {
await new Promise(r => setTimeout(r, 1000));
}
}
throw new Error(`Failed after ${maxRetries} attempts`);
}Pattern 4: Error Logging
const state = asyncState(null, {
onError: (error) => {
// Custom error logging
console.error('Operation failed:', error);
// Send to error tracking service
if (typeof Sentry !== 'undefined') {
Sentry.captureException(error);
}
// Log to analytics
logEvent('async_error', {
message: error.message,
stack: error.stack
});
}
});
await execute(state, async () => {
throw new Error('Something broke');
});
// Error automatically loggedAdvanced Usage
Custom Error Objects
class APIError extends Error {
constructor(message, status, endpoint) {
super(message);
this.name = 'APIError';
this.status = status;
this.endpoint = endpoint;
}
}
const state = asyncState(null);
await execute(state, async () => {
const response = await fetch('/api/users');
if (!response.ok) {
throw new APIError(
'Failed to fetch users',
response.status,
'/api/users'
);
}
return response.json();
});
if (state.error instanceof APIError) {
console.error(`API Error ${state.error.status} at ${state.error.endpoint}`);
}Validation Errors
class ValidationError extends Error {
constructor(message, fields) {
super(message);
this.name = 'ValidationError';
this.fields = fields;
}
}
const state = asyncState(null);
await execute(state, async () => {
const formData = { email: 'invalid', age: -5 };
const errors = {};
if (!formData.email.includes('@')) {
errors.email = 'Invalid email';
}
if (formData.age < 0) {
errors.age = 'Age cannot be negative';
}
if (Object.keys(errors).length > 0) {
throw new ValidationError('Validation failed', errors);
}
return formData;
});
if (state.error instanceof ValidationError) {
console.error('Validation errors:', state.error.fields);
}Edge Cases
Gotcha 1: Error Persists Until Cleared
const state = asyncState(null);
await execute(state, async () => {
throw new Error('Failed');
});
console.log(state.error); // Error: Failed
// Error still there after time passes
await new Promise(r => setTimeout(r, 5000));
console.log(state.error); // Still Error: Failed
// Cleared by new execute or reset
reset(state);
console.log(state.error); // nullGotcha 2: AbortError Not Captured
const state = asyncState(null);
execute(state, async (signal) => {
await new Promise(r => setTimeout(r, 5000));
return 'data';
});
// Abort after 1 second
setTimeout(() => abort(state), 1000);
setTimeout(() => {
console.log(state.error); // null (abort not an error)
console.log(state.data); // null
}, 2000);Key insight: Aborted operations don't set error because abort is intentional.
Gotcha 3: Synchronous Errors
const state = asyncState(null);
await execute(state, () => {
// Synchronous function that throws
throw new Error('Sync error');
});
console.log(state.error); // Error: Sync errorKey insight: Both sync and async errors are captured.
When to Use
Use error for:
- ✅ Displaying error messages to users
- ✅ Conditional rendering based on error state
- ✅ Error logging and tracking
- ✅ Retry logic decisions
- ✅ Error-specific UI (error pages, banners)
Don't use for:
- ❌ Success/failure boolean (use
isErrororisSuccess) - ❌ Loading state (use
loading) - ❌ Data storage (use
data)
Summary
What it is: Property holding error objects from failed operations
Initial value: null
Set when: execute() throws an error
Cleared when: New execute() starts, or reset() is called
Type: Error | null
Reactive: Yes—watchers and effects track changes
Read/write: Both (typically set by execute())
Quick Reference
// Create async state
const state = asyncState(null);
// After failed execute
await execute(state, async () => {
throw new Error('Failed!');
});
// Check error
if (state.error) {
console.error(state.error.message);
console.error(state.error.stack);
}
// Watch errors
watch(state, {
error: (err) => {
if (err) {
console.error('Error:', err.message);
}
}
});
// Clear error
reset(state);
console.log(state.error); // nullOne-Line Rule: error holds exceptions from failed operations—null when OK, Error object when failed, and cleared when retrying.