Skip to content

异步编程

JavaScript 是单线程语言,异步编程是处理耗时操作的关键技术。

回调函数

回调函数是最基本的异步处理方式:

javascript
// setTimeout
setTimeout(function() {
    console.log('1秒后执行');
}, 1000);

// 事件监听
button.addEventListener('click', function() {
    console.log('按钮被点击');
});

// 回调地狱(不推荐)
getData(function(a) {
    getMoreData(a, function(b) {
        getMoreData(b, function(c) {
            console.log(c);
        });
    });
});

Promise

Promise 是异步编程的一种解决方案:

创建 Promise

javascript
const promise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('成功');
        } else {
            reject('失败');
        }
    }, 1000);
});

使用 Promise

javascript
promise
    .then(result => {
        console.log('成功:', result);
        return '处理后的结果';
    })
    .then(result => {
        console.log('链式调用:', result);
    })
    .catch(error => {
        console.error('失败:', error);
    })
    .finally(() => {
        console.log('无论成功失败都执行');
    });

Promise 静态方法

javascript
// Promise.resolve
Promise.resolve('立即成功').then(console.log);

// Promise.reject
Promise.reject('立即失败').catch(console.error);

// Promise.all - 全部成功才成功
Promise.all([
    fetch('/api/users'),
    fetch('/api/posts')
])
.then(([users, posts]) => {
    console.log(users, posts);
})
.catch(error => {
    console.error('有一个失败:', error);
});

// Promise.allSettled - 等待所有完成
Promise.allSettled([
    Promise.resolve(1),
    Promise.reject('error'),
    Promise.resolve(3)
])
.then(results => {
    console.log(results);
    // [
    //   { status: 'fulfilled', value: 1 },
    //   { status: 'rejected', reason: 'error' },
    //   { status: 'fulfilled', value: 3 }
    // ]
});

// Promise.race - 返回最快的结果
Promise.race([
    fetch('/api/fast'),
    fetch('/api/slow')
])
.then(result => {
    console.log('最快的响应:', result);
});

// Promise.any - 返回第一个成功
Promise.any([
    Promise.reject('error1'),
    Promise.resolve('success'),
    Promise.reject('error2')
])
.then(result => {
    console.log('第一个成功:', result); // 'success'
});

封装 Promise

javascript
// 封装 XMLHttpRequest
function ajax(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onload = () => {
            if (xhr.status === 200) {
                resolve(xhr.responseText);
            } else {
                reject(new Error(xhr.statusText));
            }
        };
        xhr.onerror = () => reject(new Error('网络错误'));
        xhr.send();
    });
}

// 使用
ajax('/api/data')
    .then(data => console.log(data))
    .catch(error => console.error(error));

async/await

async/await 是 Promise 的语法糖,让异步代码看起来像同步代码:

async 函数

javascript
async function fetchData() {
    return '数据';
}

// 返回 Promise
fetchData().then(console.log); // '数据'

await 表达式

javascript
async function getData() {
    try {
        const response = await fetch('/api/data');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('错误:', error);
    }
}

并行执行

javascript
async function getParallel() {
    // 串行执行(慢)
    const user = await fetch('/api/user');
    const posts = await fetch('/api/posts');
    
    // 并行执行(快)
    const [userRes, postsRes] = await Promise.all([
        fetch('/api/user'),
        fetch('/api/posts')
    ]);
}

错误处理

javascript
async function handleError() {
    try {
        const data = await fetchData();
        return data;
    } catch (error) {
        console.error('错误:', error);
        throw error; // 重新抛出
    }
}

// 或使用 .catch()
async function getData() {
    const data = await fetchData().catch(error => {
        console.error('错误:', error);
        return null; // 默认值
    });
    return data;
}

循环中的 await

javascript
// 串行执行
async function processSequential(items) {
    for (const item of items) {
        await processItem(item);
    }
}

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

// 限制并发数
async function processWithLimit(items, limit = 3) {
    const results = [];
    for (let i = 0; i < items.length; i += limit) {
        const batch = items.slice(i, i + limit);
        const batchResults = await Promise.all(
            batch.map(item => processItem(item))
        );
        results.push(...batchResults);
    }
    return results;
}

定时器

setTimeout

javascript
// 延迟执行
const timeoutId = setTimeout(() => {
    console.log('延迟执行');
}, 1000);

// 取消
clearTimeout(timeoutId);

setInterval

javascript
// 定时执行
const intervalId = setInterval(() => {
    console.log('定时执行');
}, 1000);

// 取消
clearInterval(intervalId);

延时函数封装

javascript
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 使用
async function example() {
    console.log('开始');
    await delay(1000);
    console.log('1秒后');
}

Fetch API

基本用法

javascript
// GET 请求
fetch('/api/data')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));

// async/await
async function getData() {
    try {
        const response = await fetch('/api/data');
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('请求失败:', error);
    }
}

POST 请求

javascript
async function postData(url, data) {
    const response = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    });
    return response.json();
}

// 使用
postData('/api/users', { name: '张三', age: 25 })
    .then(data => console.log(data));

完整配置

javascript
fetch(url, {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer token'
    },
    body: JSON.stringify(data),
    mode: 'cors',
    credentials: 'include',
    cache: 'default',
    redirect: 'follow',
    signal: controller.signal
});

超时处理

javascript
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;
    } catch (error) {
        clearTimeout(timeoutId);
        if (error.name === 'AbortError') {
            throw new Error('请求超时');
        }
        throw error;
    }
}

事件循环

JavaScript 使用事件循环处理异步操作:

javascript
console.log('1');

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

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

console.log('4');

// 输出顺序: 1, 4, 3, 2
// 同步代码 -> 微任务(Promise) -> 宏任务(setTimeout)

宏任务和微任务

javascript
// 宏任务
setTimeout(() => {}, 0);
setInterval(() => {}, 0);
setImmediate(() => {}); // Node.js
requestAnimationFrame(() => {});

// 微任务
Promise.resolve().then(() => {});
queueMicrotask(() => {});
process.nextTick(() => {}); // Node.js

实践示例

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>异步编程示例</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        
        .demo-box {
            padding: 20px;
            background: #f8f9fa;
            border-radius: 8px;
            margin: 20px 0;
        }
        
        .btn {
            padding: 10px 20px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin: 5px;
        }
        
        .btn:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        
        .progress {
            height: 20px;
            background: #e9ecef;
            border-radius: 10px;
            overflow: hidden;
            margin: 10px 0;
        }
        
        .progress-bar {
            height: 100%;
            background: linear-gradient(90deg, #007bff, #0056b3);
            transition: width 0.3s;
        }
        
        .result {
            padding: 10px;
            background: white;
            border: 1px solid #ddd;
            border-radius: 4px;
            margin-top: 10px;
            min-height: 50px;
        }
        
        .loading {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 2px solid #f3f3f3;
            border-top: 2px solid #007bff;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <h1>异步编程示例</h1>
    
    <h2>Promise 示例</h2>
    <div class="demo-box">
        <button class="btn" onclick="promiseDemo()">执行 Promise</button>
        <div class="result" id="promiseResult">点击按钮查看结果</div>
    </div>
    
    <h2>async/await 示例</h2>
    <div class="demo-box">
        <button class="btn" id="asyncBtn" onclick="asyncDemo()">执行 async/await</button>
        <div class="progress">
            <div class="progress-bar" id="progressBar" style="width: 0%"></div>
        </div>
        <div class="result" id="asyncResult">点击按钮查看结果</div>
    </div>
    
    <h2>并行请求示例</h2>
    <div class="demo-box">
        <button class="btn" onclick="parallelDemo()">并行请求</button>
        <button class="btn" onclick="sequentialDemo()">串行请求</button>
        <div class="result" id="requestResult">点击按钮查看结果</div>
    </div>
    
    <script>
        // Promise 示例
        function promiseDemo() {
            const result = document.getElementById('promiseResult');
            result.innerHTML = '<span class="loading"></span> 执行中...';
            
            new Promise((resolve) => {
                setTimeout(() => resolve('第一步完成'), 1000);
            })
            .then(result => {
                return new Promise(resolve => {
                    setTimeout(() => resolve(result + ' -> 第二步完成'), 1000);
                });
            })
            .then(result => {
                return new Promise(resolve => {
                    setTimeout(() => resolve(result + ' -> 第三步完成'), 1000);
                });
            })
            .then(finalResult => {
                document.getElementById('promiseResult').textContent = finalResult;
            });
        }
        
        // async/await 示例
        async function asyncDemo() {
            const btn = document.getElementById('asyncBtn');
            const progressBar = document.getElementById('progressBar');
            const result = document.getElementById('asyncResult');
            
            btn.disabled = true;
            result.innerHTML = '<span class="loading"></span> 执行中...';
            
            try {
                // 模拟进度
                for (let i = 0; i <= 100; i += 10) {
                    await delay(200);
                    progressBar.style.width = i + '%';
                }
                
                // 模拟数据获取
                const data = await mockFetch();
                result.textContent = '获取数据成功: ' + JSON.stringify(data);
            } catch (error) {
                result.textContent = '错误: ' + error.message;
            } finally {
                btn.disabled = false;
            }
        }
        
        // 延时函数
        function delay(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
        
        // 模拟 API 请求
        function mockFetch() {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve({
                        id: 1,
                        name: '张三',
                        email: 'zhangsan@example.com'
                    });
                }, 500);
            });
        }
        
        // 并行请求
        async function parallelDemo() {
            const result = document.getElementById('requestResult');
            result.innerHTML = '<span class="loading"></span> 执行中...';
            
            const start = Date.now();
            
            const [user, posts, comments] = await Promise.all([
                mockFetch(),
                mockFetch(),
                mockFetch()
            ]);
            
            const time = Date.now() - start;
            result.textContent = `并行完成,耗时: ${time}ms\n` +
                `用户: ${user.name}, 文章: ${posts.id}, 评论: ${comments.id}`;
        }
        
        // 串行请求
        async function sequentialDemo() {
            const result = document.getElementById('requestResult');
            result.innerHTML = '<span class="loading"></span> 执行中...';
            
            const start = Date.now();
            
            const user = await mockFetch();
            const posts = await mockFetch();
            const comments = await mockFetch();
            
            const time = Date.now() - start;
            result.textContent = `串行完成,耗时: ${time}ms\n` +
                `用户: ${user.name}, 文章: ${posts.id}, 评论: ${comments.id}`;
        }
    </script>
</body>
</html>