02-异步编程

📋 学习目标

  • 理解JavaScript事件循环机制

  • 掌握Promise的使用和原理

  • 精通async/await异步语法

  • 理解并发控制和错误处理

🔄 事件循环

执行模型

// 同步任务
console.log('1');

// 宏任务(MacroTask)
setTimeout(() => {
    console.log('2');
}, 0);

// 微任务(MicroTask)
Promise.resolve().then(() => {
    console.log('3');
});

console.log('4');

// 输出顺序:1 4 3 2

任务队列

/*
执行顺序:
1. 同步代码
2. 微任务队列(Promise, MutationObserver)
3. 宏任务队列(setTimeout, setInterval, I/O)
*/

console.log('script start');

setTimeout(() => {
    console.log('setTimeout');
}, 0);

Promise.resolve()
    .then(() => {
        console.log('promise1');
    })
    .then(() => {
        console.log('promise2');
    });

console.log('script end');

// 输出:
// script start
// script end
// promise1
// promise2
// setTimeout

📞 回调函数

基本回调

// 简单回调
function getData(callback) {
    setTimeout(() => {
        callback({name: 'John', age: 30});
    }, 1000);
}

getData((data) => {
    console.log(data);
});

// Node.js错误优先回调
fs.readFile('file.txt', (err, data) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(data);
});

回调地狱

// 回调地狱示例
getUser(userId, (err, user) => {
    if (err) {
        handleError(err);
    } else {
        getOrders(user.id, (err, orders) => {
            if (err) {
                handleError(err);
            } else {
                getOrderDetails(orders[0].id, (err, details) => {
                    if (err) {
                        handleError(err);
                    } else {
                        // 继续嵌套...
                    }
                });
            }
        });
    }
});

🎁 Promise

基本用法

// 创建Promise
const promise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('Success!');
        } else {
            reject(new Error('Failed!'));
        }
    }, 1000);
});

// 使用Promise
promise
    .then(result => {
        console.log(result); // Success!
        return 'Next value';
    })
    .then(result => {
        console.log(result); // Next value
    })
    .catch(error => {
        console.error(error);
    })
    .finally(() => {
        console.log('Cleanup');
    });

Promise状态

/*
三种状态:
1. pending(进行中)
2. fulfilled(已成功)
3. rejected(已失败)

状态只能从pending改变为fulfilled或rejected
状态一旦改变就不会再变
*/

const p1 = Promise.resolve('Success'); // 立即fulfilled
const p2 = Promise.reject(new Error('Failed')); // 立即rejected

链式调用

function getUser(id) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({id, name: 'John'});
        }, 1000);
    });
}

function getOrders(userId) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve([{id: 1}, {id: 2}]);
        }, 1000);
    });
}

// 链式调用
getUser(1)
    .then(user => {
        console.log('User:', user);
        return getOrders(user.id);
    })
    .then(orders => {
        console.log('Orders:', orders);
    })
    .catch(error => {
        console.error('Error:', error);
    });

Promise静态方法

// Promise.all - 所有成功才成功
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3])
    .then(results => {
        console.log(results); // [1, 2, 3]
    })
    .catch(error => {
        console.error('One failed:', error);
    });

// Promise.allSettled - 等待所有完成
Promise.allSettled([p1, p2, Promise.reject('error')])
    .then(results => {
        results.forEach(result => {
            if (result.status === 'fulfilled') {
                console.log('Success:', result.value);
            } else {
                console.log('Failed:', result.reason);
            }
        });
    });

// Promise.race - 最快的一个
Promise.race([
    fetch('/api/slow'),
    new Promise((_, reject) => 
        setTimeout(() => reject(new Error('Timeout')), 3000)
    )
])
    .then(result => console.log(result))
    .catch(error => console.error(error));

// Promise.any - 任意一个成功
Promise.any([
    fetch('/api/1').catch(() => 'API1 failed'),
    fetch('/api/2').catch(() => 'API2 failed'),
    fetch('/api/3').catch(() => 'API3 failed')
])
    .then(result => console.log('First success:', result))
    .catch(() => console.log('All failed'));

Promise实现

class MyPromise {
    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(fn => fn());
            }
        };
        
        const reject = (reason) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };
        
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' 
            ? onFulfilled 
            : value => value;
        onRejected = typeof onRejected === 'function' 
            ? onRejected 
            : reason => { throw reason };
            
        return new MyPromise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                setTimeout(() => {
                    try {
                        const result = onFulfilled(this.value);
                        resolve(result);
                    } catch (error) {
                        reject(error);
                    }
                });
            }
            
            if (this.state === 'rejected') {
                setTimeout(() => {
                    try {
                        const result = onRejected(this.reason);
                        resolve(result);
                    } catch (error) {
                        reject(error);
                    }
                });
            }
            
            if (this.state === 'pending') {
                this.onFulfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const result = onFulfilled(this.value);
                            resolve(result);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
                
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const result = onRejected(this.reason);
                            resolve(result);
                        } catch (error) {
                            reject(error);
                        }
                    });
                });
            }
        });
    }
}

🚀 Async/Await

基本用法

// async函数返回Promise
async function getData() {
    return 'data';
}

getData().then(data => console.log(data)); // data

// await等待Promise
async function fetchUser() {
    const response = await fetch('/api/user');
    const user = await response.json();
    return user;
}

// 等同于
function fetchUser() {
    return fetch('/api/user')
        .then(response => response.json());
}

错误处理

// try-catch捕获错误
async function getData() {
    try {
        const response = await fetch('/api/data');
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Error:', error);
        return null;
    }
}

// 统一错误处理
async function request(url) {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
    }
    return response.json();
}

async function main() {
    try {
        const data = await request('/api/data');
        console.log(data);
    } catch (error) {
        console.error('Failed:', error.message);
    }
}

// 顶层await(ES2022)
const data = await fetch('/api/data').then(r => r.json());

并发执行

// ❌ 串行执行(慢)
async function sequential() {
    const user = await fetchUser();      // 1秒
    const orders = await fetchOrders();  // 1秒
    const products = await fetchProducts(); // 1秒
    return {user, orders, products};     // 总共3秒
}

// ✅ 并发执行(快)
async function concurrent() {
    const [user, orders, products] = await Promise.all([
        fetchUser(),
        fetchOrders(),
        fetchProducts()
    ]);
    return {user, orders, products};     // 总共1秒
}

// 部分依赖
async function partial() {
    const user = await fetchUser();
    // 这两个可以并发
    const [orders, profile] = await Promise.all([
        fetchOrders(user.id),
        fetchProfile(user.id)
    ]);
    return {user, orders, profile};
}

循环中的异步

// ❌ forEach不等待
async function wrong() {
    const ids = [1, 2, 3];
    ids.forEach(async (id) => {
        const data = await fetchData(id);
        console.log(data);
    });
    console.log('Done'); // 会立即执行
}

// ✅ for...of串行执行
async function serial() {
    const ids = [1, 2, 3];
    for (const id of ids) {
        const data = await fetchData(id);
        console.log(data);
    }
    console.log('Done');
}

// ✅ Promise.all并发执行
async function parallel() {
    const ids = [1, 2, 3];
    const promises = ids.map(id => fetchData(id));
    const results = await Promise.all(promises);
    results.forEach(data => console.log(data));
    console.log('Done');
}

// ✅ map + Promise.all
async function mapAsync() {
    const ids = [1, 2, 3];
    const results = await Promise.all(
        ids.map(async (id) => {
            const data = await fetchData(id);
            return processData(data);
        })
    );
    return results;
}

🔧 实战技巧

超时控制

function timeout(ms) {
    return new Promise((_, reject) => {
        setTimeout(() => reject(new Error('Timeout')), ms);
    });
}

async function fetchWithTimeout(url, ms = 3000) {
    return Promise.race([
        fetch(url),
        timeout(ms)
    ]);
}

// 使用
try {
    const response = await fetchWithTimeout('/api/data', 5000);
    const data = await response.json();
} catch (error) {
    console.error('Request failed or timeout:', error);
}

重试机制

async function retry(fn, maxAttempts = 3, delay = 1000) {
    for (let i = 0; i < maxAttempts; i++) {
        try {
            return await fn();
        } catch (error) {
            if (i === maxAttempts - 1) throw error;
            console.log(`Attempt ${i + 1} failed, retrying...`);
            await new Promise(resolve => setTimeout(resolve, delay));
        }
    }
}

// 使用
const data = await retry(() => fetch('/api/data'), 3, 2000);

并发限制

class PromisePool {
    constructor(maxConcurrency) {
        this.maxConcurrency = maxConcurrency;
        this.currentCount = 0;
        this.queue = [];
    }
    
    async add(promiseFactory) {
        while (this.currentCount >= this.maxConcurrency) {
            await Promise.race(this.queue);
        }
        
        this.currentCount++;
        const promise = promiseFactory();
        const queueItem = promise.then(
            () => this.remove(queueItem),
            () => this.remove(queueItem)
        );
        this.queue.push(queueItem);
        
        return promise;
    }
    
    remove(promise) {
        const index = this.queue.indexOf(promise);
        this.queue.splice(index, 1);
        this.currentCount--;
    }
}

// 使用
const pool = new PromisePool(3);
const tasks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const results = await Promise.all(
    tasks.map(id => pool.add(() => fetchData(id)))
);

取消请求

// 使用AbortController
const controller = new AbortController();
const signal = controller.signal;

fetch('/api/data', {signal})
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => {
        if (error.name === 'AbortError') {
            console.log('Request cancelled');
        }
    });

// 取消请求
controller.abort();

// 超时取消
async function fetchWithAbort(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 await response.json();
    } catch (error) {
        if (error.name === 'AbortError') {
            throw new Error('Request timeout');
        }
        throw error;
    }
}

缓存Promise

const cache = new Map();

function getCachedData(key, fetcher) {
    if (!cache.has(key)) {
        cache.set(key, fetcher());
    }
    return cache.get(key);
}

// 使用
const userData = await getCachedData('user:1', () => fetchUser(1));

// 带过期时间的缓存
class PromiseCache {
    constructor(ttl = 60000) {
        this.cache = new Map();
        this.ttl = ttl;
    }
    
    async get(key, fetcher) {
        const cached = this.cache.get(key);
        if (cached && Date.now() - cached.timestamp < this.ttl) {
            return cached.value;
        }
        
        const value = await fetcher();
        this.cache.set(key, {
            value,
            timestamp: Date.now()
        });
        return value;
    }
}

📚 实践练习

练习1:Promise实现

手动实现:

  • Promise.all

  • Promise.race

  • Promise.allSettled

  • Promise.any

练习2:异步工具函数

实现以下工具:

  • 带超时的fetch

  • 重试机制

  • 并发控制

  • 请求队列

练习3:实际应用

创建一个数据获取层:

  • 支持缓存

  • 支持取消

  • 支持重试

  • 错误处理

💡 最佳实践

  1. 优先使用async/await而不是Promise链

  2. 注意Promise.all的失败处理

  3. 合理使用并发和串行

  4. 总是处理异步错误

  5. 避免在循环中使用await(除非需要串行)

📚 参考资料