JavaScript 异步编程详解
异步编程是 JavaScript 的核心特性之一,它允许程序在等待某些操作完成的同时继续执行其他任务。本文将详细介绍 JavaScript 异步编程的各种方式和最佳实践。
基础概念
1. 同步 vs 异步
console.log('1'); console.log('2'); console.log('3');
console.log('1'); setTimeout(() => console.log('2'), 1000); console.log('3');
|
2. 事件循环
console.log('1');
Promise.resolve().then(() => console.log('2'));
setTimeout(() => console.log('3'), 0);
console.log('4');
|
3. 调用栈、任务队列和事件循环
回调函数
1. 基本用法
function fetchData(callback) { setTimeout(() => { callback('Data fetched successfully'); }, 1000); }
fetchData((data) => { console.log(data); });
|
2. 回调地狱
function fetchData(callback) { setTimeout(() => { callback('Data fetched'); }, 1000); }
function processData(data, callback) { setTimeout(() => { callback(`Processed: ${data}`); }, 1000); }
function saveData(processedData, callback) { setTimeout(() => { callback(`Saved: ${processedData}`); }, 1000); }
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (savedData) => { console.log(savedData); }); }); });
|
3. 错误处理
function fetchData(callback) { setTimeout(() => { const success = Math.random() > 0.5; if (success) { callback(null, 'Data fetched successfully'); } else { callback(new Error('Failed to fetch data')); } }, 1000); }
fetchData((error, data) => { if (error) { console.error('Error:', error.message); } else { console.log('Success:', data); } });
|
Promise
1. 基础用法
const fetchData = new Promise((resolve, reject) => { setTimeout(() => { const success = Math.random() > 0.5; if (success) { resolve('Data fetched successfully'); } else { reject(new Error('Failed to fetch data')); } }, 1000); });
fetchData .then(data => { console.log('Success:', data); return 'Processed: ' + data; }) .then(processedData => { console.log('Processed:', processedData); }) .catch(error => { console.error('Error:', error.message); }) .finally(() => { console.log('Operation completed'); });
|
2. Promise 静态方法
const resolvedPromise = Promise.resolve('Resolved value');
const rejectedPromise = Promise.reject('Rejected reason');
const promises = [ Promise.resolve(1), Promise.resolve(2), Promise.resolve(3) ];
Promise.all(promises) .then(values => { console.log('All values:', values); });
const mixedPromises = [ Promise.resolve(1), Promise.reject('Error'), Promise.resolve(3) ];
Promise.allSettled(mixedPromises) .then(results => { console.log('All settled:', results); });
const racePromises = [ new Promise(resolve => setTimeout(() => resolve('Fast'), 100)), new Promise(resolve => setTimeout(() => resolve('Slow'), 200)) ];
Promise.race(racePromises) .then(result => { console.log('Race winner:', result); });
|
3. 实际应用
function fetchUser(id) { return new Promise((resolve, reject) => { setTimeout(() => { if (id > 0) { resolve({ id, name: 'User ' + id }); } else { reject(new Error('Invalid user ID')); } }, 1000); }); }
function fetchPosts(userId) { return new Promise((resolve, reject) => { setTimeout(() => { resolve([ { id: 1, title: 'Post 1', userId }, { id: 2, title: 'Post 2', userId } ]); }, 1000); }); }
fetchUser(1) .then(user => { console.log('User:', user); return fetchPosts(user.id); }) .then(posts => { console.log('Posts:', posts); }) .catch(error => { console.error('Error:', error.message); });
|
Async/Await
1. 基础语法
async function getUserData(userId) { try { const user = await fetchUser(userId); console.log('User:', user); const posts = await fetchPosts(user.id); console.log('Posts:', posts); return { user, posts }; } catch (error) { console.error('Error:', error.message); throw error; } }
getUserData(1).then(data => { console.log('Complete data:', data); });
|
2. 并行操作
async function getMultipleData() { try { const [user1, user2] = await Promise.all([ fetchUser(1), fetchUser(2) ]); console.log('Users:', user1, user2); const [posts1, posts2] = await Promise.all([ fetchPosts(user1.id), fetchPosts(user2.id) ]); console.log('Posts:', posts1, posts2); } catch (error) { console.error('Error:', error.message); } }
|
3. 循环中的异步
async function processUsers() { const userIds = [1, 2, 3]; for (const userId of userIds) { const user = await fetchUser(userId); console.log('Processed user:', user); } const users = await Promise.all( userIds.map(userId => fetchUser(userId)) ); console.log('All users:', users); }
|
高级异步模式
1. 自定义 Promise
class CustomPromise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onFulfilledCallbacks.forEach(callback => callback(value)); } }; const reject = (reason) => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(callback => callback(reason)); } }; try { executor(resolve, reject); } catch (error) { reject(error); } } then(onFulfilled, onRejected) { return new CustomPromise((resolve, reject) => { const handleFulfilled = (value) => { try { if (typeof onFulfilled === 'function') { resolve(onFulfilled(value)); } else { resolve(value); } } catch (error) { reject(error); } }; const handleRejected = (reason) => { try { if (typeof onRejected === 'function') { resolve(onRejected(reason)); } else { reject(reason); } } catch (error) { reject(error); } }; if (this.state === 'fulfilled') { setTimeout(() => handleFulfilled(this.value), 0); } else if (this.state === 'rejected') { setTimeout(() => handleRejected(this.reason), 0); } else { this.onFulfilledCallbacks.push(handleFulfilled); this.onRejectedCallbacks.push(handleRejected); } }); } catch(onRejected) { return this.then(null, onRejected); } }
|
2. Promise 池
class PromisePool { constructor(maxConcurrency) { this.maxConcurrency = maxConcurrency; this.running = 0; this.queue = []; } async add(task) { return new Promise((resolve, reject) => { this.queue.push({ task, resolve, reject }); this.process(); }); } async process() { while (this.running < this.maxConcurrency && this.queue.length > 0) { const { task, resolve, reject } = this.queue.shift(); this.running++; try { const result = await task(); resolve(result); } catch (error) { reject(error); } finally { this.running--; this.process(); } } } }
const pool = new PromisePool(3);
for (let i = 0; i < 10; i++) { pool.add(() => { return new Promise(resolve => { setTimeout(() => { console.log(`Task ${i} completed`); resolve(i); }, 1000); }); }); }
|
3. 自动重试机制
async function retry(fn, maxRetries = 3, delay = 1000) { let lastError; for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { lastError = error; console.log(`Attempt ${i + 1} failed:`, error.message); if (i < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, delay)); } } } throw lastError; }
async function unreliableOperation() { if (Math.random() > 0.5) { throw new Error('Operation failed'); } return 'Success'; }
retry(unreliableOperation, 3, 1000) .then(result => console.log('Result:', result)) .catch(error => console.error('Failed after retries:', error.message));
|
实际应用场景
1. API 请求
class ApiClient { constructor(baseURL) { this.baseURL = baseURL; } async request(endpoint, options = {}) { const url = `${this.baseURL}${endpoint}`; try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('API request failed:', error); throw error; } } get(endpoint) { return this.request(endpoint); } post(endpoint, data) { return this.request(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); } }
const api = new ApiClient('https://api.example.com');
async function loadUserData() { try { const user = await api.get('/users/1'); const posts = await api.get(`/users/${user.id}/posts`); console.log('User data:', user); console.log('Posts:', posts); } catch (error) { console.error('Failed to load user data:', error); } }
|
2. 文件处理
async function readFile(filePath) { return new Promise((resolve, reject) => { const fs = require('fs'); fs.readFile(filePath, 'utf8', (err, data) => { if (err) { reject(err); } else { resolve(data); } }); }); }
async function writeFile(filePath, content) { return new Promise((resolve, reject) => { const fs = require('fs'); fs.writeFile(filePath, content, 'utf8', (err) => { if (err) { reject(err); } else { resolve(); } }); }); }
async function processFile() { try { const content = await readFile('input.txt'); const processed = content.toUpperCase(); await writeFile('output.txt', processed); console.log('File processed successfully'); } catch (error) { console.error('File processing failed:', error); } }
|
3. 数据流处理
async function processStream(stream) { const chunks = []; for await (const chunk of stream) { chunks.push(chunk); } return Buffer.concat(chunks).toString(); }
const fs = require('fs'); const stream = fs.createReadStream('large-file.txt');
processStream(stream) .then(data => { console.log('File content:', data); }) .catch(error => { console.error('Stream processing failed:', error); });
|
最佳实践
1. 错误处理
async function fetchData() { try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error('Fetch failed:', error); return { error: 'Failed to fetch data' }; } }
async function fetchData() { const response = await fetch('/api/data'); return response.json(); }
|
2. 资源清理
async function processFile() { const file = fs.open('test.txt', 'r'); try { const content = await file.readFile(); } finally { await file.close(); } }
|
3. 并发控制
async function fetchMultipleUrls(urls, maxConcurrent = 5) { const results = []; const executing = new Set(); for (const url of urls) { if (executing.size >= maxConcurrent) { await Promise.race(executing); } const promise = fetch(url) .then(response => response.json()) .catch(error => ({ error: error.message })); executing.add(promise); promise.finally(() => executing.delete(promise)); results.push(promise); } return Promise.all(results); }
|
4. 超时处理
async function fetchWithTimeout(url, timeout = 5000) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { signal: controller.signal }); clearTimeout(timeoutId); return response.json(); } catch (error) { if (error.name === 'AbortError') { throw new Error('Request timeout'); } throw error; } }
|
总结
JavaScript 异步编程经历了从回调函数到 Promise,再到 async/await 的演进过程。每个阶段都有其适用场景:
- 回调函数:简单场景,避免回调地狱
- Promise:更清晰的错误处理,避免回调地狱
- async/await:更直观的异步代码写法
- 高级模式:特殊场景的自定义实现
在实际开发中,应该根据具体需求选择合适的异步编程方式,并遵循最佳实践来确保代码的可维护性和可靠性。