Implementing Ajax Requests with ES6 Promises and Express CORS Configuration
Core Concepts of ES6 Promises
A Promise represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It addressse callback hell by providing a structured way to handle async flows.
Basic Promise Execution Flow
Consider an asynchronous task using setTimeout:
// Traditional approach lacks error handling clarity
setTimeout(() => console.log('Operation completed'), 1000);
Rewriting with Promises:
const asyncTask = new Promise((onFulfilled, onRejected) => {
setTimeout(() => {
const success = Math.random() > 0.3; // Simulate success/failure
success ? onFulfilled('Operation succeeded') : onRejected('Operation failed');
}, 1000);
});
asyncTask.then(
result => console.log(result), // Handles fulfillment
error => console.error(error) // Handles rejection
);
Ajax Request Wrapper with Promises
Setting Up Express Server with CORS
Create a server to handle cross-origin requests:
import express from 'express';
const serverApp = express();
// Configure CORS middleware
serverApp.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
if (req.method === 'OPTIONS') return res.sendStatus(204);
next();
});
// Sample API endpoint
severApp.get('/api/users', (req, res) => {
res.json({
status: 'success',
data: { id: 1, name: 'Alex', role: 'Developer' }
});
});
serverApp.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
Ajax Implementation Comparison
Traditional XMLHttpRequest Approach
const httpReq = new XMLHttpRequest();
httpReq.open('GET', 'http://localhost:3000/api/users');
httpReq.send();
httpReq.onload = function() {
if (this.status === 200) {
console.log('Response:', this.responseText);
} else {
console.error(`Request failed with status: ${this.status}`);
}
};
Promise-Based Wrapper
function fetchWithPromise(url) {
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.open('GET', url);
request.send();
request.onload = () => {
if (request.status >= 200 && request.status < 300) {
resolve(JSON.parse(request.response));
} else {
reject(new Error(`HTTP error! Status: ${request.status}`));
}
};
request.onerror = () => reject(new Error('Network request failed'));
});
}
// Usage example
fetchWithPromise('http://localhost:3000/api/users')
.then(data => console.log('User data:', data))
.catch(err => console.error('Fetch error:', err.message));
Code Structure Optimization
The Promise wrapper encapsulates error handling and state management, eliminating nested callbacks. Key improvements include:
- Centralized success/error handling via
.then()and.catch() - Explicit state transitions through
resolve/reject - Compatibility with modern async/await syntax