Skip to content

异步编程

概述

异步编程是JavaScript中处理非阻塞操作的核心技术,允许程序在等待耗时操作(如网络请求、文件读写)完成时继续执行其他任务。

JavaScript异步编程演进

1. 回调函数(Callback)

javascript
function fetchData(callback) {
    setTimeout(() => {
        callback('数据加载完成');
    }, 1000);
}

fetchData((result) => {
    console.log(result);
});

回调地狱问题:

javascript
getUser(userId, function(user) {
    getPosts(user.id, function(posts) {
        getComments(posts[0].id, function(comments) {
            // 嵌套层级过深
        });
    });
});

2. Promise

javascript
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('数据加载完成');
        }, 1000);
    });
}

fetchData()
    .then(result => console.log(result))
    .catch(error => console.error(error));

3. async/await

javascript
async function loadData() {
    try {
        const result = await fetchData();
        console.log(result);
    } catch (error) {
        console.error(error);
    }
}

Promise详解

创建Promise

javascript
const promise = new Promise((resolve, reject) => {
    // 异步操作
    if (success) {
        resolve(value);
    } else {
        reject(error);
    }
});

Promise方法

then()

javascript
promise.then(
    value => console.log('成功:', value),
    error => console.log('失败:', error)
);

catch()

javascript
promise
    .then(value => console.log(value))
    .catch(error => console.error(error));

finally()

javascript
promise
    .then(value => console.log(value))
    .catch(error => console.error(error))
    .finally(() => console.log('操作完成'));

Promise静态方法

Promise.all()

javascript
const promises = [
    fetch('/api/user'),
    fetch('/api/posts'),
    fetch('/api/comments')
];

Promise.all(promises)
    .then(results => {
        console.log('所有请求完成:', results);
    })
    .catch(error => {
        console.error('有一个请求失败:', error);
    });

Promise.race()

javascript
const timeout = new Promise((_, reject) => {
    setTimeout(() => reject('请求超时'), 5000);
});

Promise.race([fetch('/api/data'), timeout])
    .then(result => console.log('请求成功:', result))
    .catch(error => console.error('错误:', error));

Promise.allSettled()

javascript
const promises = [
    Promise.resolve('成功1'),
    Promise.reject('失败1'),
    Promise.resolve('成功2')
];

Promise.allSettled(promises)
    .then(results => {
        results.forEach(result => {
            if (result.status === 'fulfilled') {
                console.log('成功:', result.value);
            } else {
                console.log('失败:', result.reason);
            }
        });
    });

async/await最佳实践

错误处理

javascript
async function fetchWithRetry(url, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url);
            return await response.json();
        } catch (error) {
            if (i === retries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * i));
        }
    }
}

并行执行

javascript
async function loadAllData() {
    const [user, posts, comments] = await Promise.all([
        fetchUser(),
        fetchPosts(),
        fetchComments()
    ]);
    
    return { user, posts, comments };
}

顺序执行

javascript
async function processSequentially() {
    const result1 = await step1();
    const result2 = await step2(result1);
    const result3 = await step3(result2);
    return result3;
}

实际应用场景

1. 网络请求

javascript
async function fetchUserData(userId) {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
        throw new Error('网络请求失败');
    }
    return await response.json();
}

2. 文件操作(Node.js)

javascript
const fs = require('fs').promises;

async function readFileContent(filePath) {
    try {
        const content = await fs.readFile(filePath, 'utf-8');
        console.log(content);
    } catch (error) {
        console.error('读取文件失败:', error);
    }
}

3. 数据库操作

javascript
async function getUserWithPosts(userId) {
    const connection = await getDatabaseConnection();
    try {
        const user = await connection.query('SELECT * FROM users WHERE id = ?', [userId]);
        const posts = await connection.query('SELECT * FROM posts WHERE user_id = ?', [userId]);
        return { user, posts };
    } finally {
        connection.release();
    }
}

性能优化技巧

1. 避免不必要的await

javascript
// 不好
async function processItems(items) {
    for (const item of items) {
        await processItem(item); // 顺序执行,性能差
    }
}

// 好
async function processItems(items) {
    const promises = items.map(item => processItem(item));
    await Promise.all(promises); // 并行执行
}

2. 使用Promise缓存

javascript
const cache = new Map();

async function getCachedData(key, fetchFunction) {
    if (cache.has(key)) {
        return cache.get(key);
    }
    
    const promise = fetchFunction();
    cache.set(key, promise);
    
    try {
        const result = await promise;
        return result;
    } catch (error) {
        cache.delete(key);
        throw error;
    }
}

3. 控制并发数量

javascript
async function limitedConcurrency(tasks, limit) {
    const results = [];
    const executing = [];
    
    for (const task of tasks) {
        const promise = task().then(result => {
            executing.splice(executing.indexOf(promise), 1);
            return result;
        });
        
        results.push(promise);
        executing.push(promise);
        
        if (executing.length >= limit) {
            await Promise.race(executing);
        }
    }
    
    return Promise.all(results);
}

常见问题与解决方案

1. 内存泄漏

javascript
// 避免在闭包中保留不必要的引用
async function processLargeDataset() {
    const data = await fetchLargeData();
    // 及时清理不再需要的数据
    processData(data);
    // data = null; // 帮助垃圾回收
}

2. 未处理的Promise拒绝

javascript
// 总是处理Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
    console.error('未处理的Promise拒绝:', reason);
});

// 或者使用catch
asyncFunction().catch(error => {
    console.error('捕获错误:', error);
});

3. 回调函数与Promise混合

javascript
// 将回调函数转换为Promise
function promisify(fn) {
    return function(...args) {
        return new Promise((resolve, reject) => {
            fn(...args, (error, result) => {
                if (error) reject(error);
                else resolve(result);
            });
        });
    };
}

工具与库推荐

1. axios

  • 基于Promise的HTTP客户端
  • 请求/响应拦截器
  • 自动转换JSON数据

2. bluebird

  • 功能丰富的Promise库
  • 性能优化
  • 额外的实用方法

3. p-limit

  • 控制Promise并发数量
  • 轻量级解决方案

学习资源

书籍

  • 《JavaScript异步编程》
  • 《深入理解Promise》

在线资源

  • MDN Web Docs: Promise
  • JavaScript.info: Async/await

本文档涵盖了JavaScript异步编程的核心概念和最佳实践,后续会补充更多高级主题和实战案例。

更新时间: