𝑻𝒆𝒏𝑪𝒍𝒂𝒘正在头脑风暴···
𝑻𝒆𝒏𝑲𝒊𝑺𝒆𝒀𝒂の𝑨𝒈𝒆𝒏𝒕助手
𝑻𝒆𝒏-𝒇𝒍𝒂𝒔𝒉

JavaScript 异步编程详解

异步编程是 JavaScript 的核心特性之一,它允许程序在等待某些操作完成的同时继续执行其他任务。本文将详细介绍 JavaScript 异步编程的各种方式和最佳实践。

基础概念

1. 同步 vs 异步

// 同步编程
console.log('1');
console.log('2');
console.log('3');
// 输出: 1, 2, 3

// 异步编程
console.log('1');
setTimeout(() => console.log('2'), 1000);
console.log('3');
// 输出: 1, 3, 2 (延迟1秒后)

2. 事件循环

console.log('1');

// 微任务
Promise.resolve().then(() => console.log('2'));

// 宏任务
setTimeout(() => console.log('3'), 0);

console.log('4');
// 输出: 1, 4, 2, 3

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 静态方法

// Promise.resolve
const resolvedPromise = Promise.resolve('Resolved value');

// Promise.reject
const rejectedPromise = Promise.reject('Rejected reason');

// Promise.all
const promises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
];

Promise.all(promises)
.then(values => {
console.log('All values:', values); // [1, 2, 3]
});

// Promise.allSettled
const mixedPromises = [
Promise.resolve(1),
Promise.reject('Error'),
Promise.resolve(3)
];

Promise.allSettled(mixedPromises)
.then(results => {
console.log('All settled:', results);
});

// Promise.race
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); // 'Fast'
});

3. 实际应用

// 模拟 API 调用
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/await 重写上面的例子
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 请求

// 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. 资源清理

// 使用 try-finally 确保资源释放
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 的演进过程。每个阶段都有其适用场景:

  1. 回调函数:简单场景,避免回调地狱
  2. Promise:更清晰的错误处理,避免回调地狱
  3. async/await:更直观的异步代码写法
  4. 高级模式:特殊场景的自定义实现

在实际开发中,应该根据具体需求选择合适的异步编程方式,并遵循最佳实践来确保代码的可维护性和可靠性。