JavaScript异步编程完全指南:从Promise到Async/Await实战 | JS高级教程

掌握现代JavaScript异步编程的核心概念和最佳实践

JavaScript异步编程简介

JavaScript是单线程语言,但通过异步编程模式可以处理耗时操作而不阻塞主线程。了解异步编程是成为高级JavaScript开发者的关键一步。

异步编程的演进

  1. 回调函数 (Callback):最基础的异步处理方式,但容易导致”回调地狱”
  2. Promise:ES6引入的更优雅的异步处理方案
  3. Async/Await:ES2017提供的让异步代码看起来像同步代码的语法糖

事件循环机制

JavaScript通过事件循环处理异步操作,理解调用栈、任务队列和微任务队列是关键:

console.log('开始');

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

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

console.log('结束');

// 输出顺序:
// 开始
// 结束
// Promise 回调 (微任务)
// setTimeout 回调 (宏任务)

Promise深入解析

Promise是表示异步操作最终完成或失败的对象,它有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

创建Promise

// 创建Promise
const fetchData = new Promise((resolve, reject) => {
    // 模拟异步操作
    setTimeout(() => {
        const success = Math.random() > 0.3;
        
        if (success) {
            resolve({ data: '获取的数据', status: 200 });
        } else {
            reject({ error: '获取数据失败', status: 500 });
        }
    }, 1000);
});

使用Promise

// 使用then和catch处理Promise
fetchData
    .then(response => {
        console.log('成功:', response);
        return processData(response.data); // 可以返回新值或新Promise
    })
    .then(processedData => {
        console.log('处理后的数据:', processedData);
    })
    .catch(error => {
        console.error('失败:', error);
    })
    .finally(() => {
        console.log('操作完成(无论成功或失败)');
    });

Promise实用方法

// Promise.all - 等待所有Promise完成
Promise.all([promise1, promise2, promise3])
    .then(results => {
        console.log('所有操作完成:', results);
    })
    .catch(error => {
        console.error('有一个操作失败:', error);
    });

// Promise.race - 竞速,第一个完成或失败的Promise
Promise.race([promise1, promise2])
    .then(result => {
        console.log('第一个完成的操作:', result);
    });

// Promise.allSettled - 等待所有Promise完成(无论成功或失败)
Promise.allSettled([promise1, promise2])
    .then(results => {
        results.forEach(result => {
            if (result.status === 'fulfilled') {
                console.log('成功:', result.value);
            } else {
                console.log('失败:', result.reason);
            }
        });
    });

Async/Await实战

Async/Await是基于Promise的语法糖,让异步代码看起来和同步代码类似,提高了代码的可读性和可维护性。

基本用法

// async函数总是返回Promise
async function fetchUserData(userId) {
    try {
        // await会暂停执行,直到Promise解决
        const response = await fetch(`/api/users/${userId}`);
        
        if (!response.ok) {
            throw new Error('网络响应不正常');
        }
        
        const userData = await response.json();
        const userPosts = await fetchUserPosts(userId);
        
        return { ...userData, posts: userPosts };
    } catch (error) {
        console.error('获取用户数据失败:', error);
        throw error; // 重新抛出错误以便外部捕获
    }
}

// 使用async函数
async function displayUserData() {
    try {
        const userData = await fetchUserData(123);
        renderUserProfile(userData);
    } catch (error) {
        showErrorMessage('无法加载用户数据');
    }
}

并行操作优化

// 顺序执行 - 慢
async function sequentialFetch() {
    const user = await fetch('/api/user');
    const posts = await fetch('/api/posts');
    const comments = await fetch('/api/comments');
    
    return { user, posts, comments };
}

// 并行执行 - 快
async function parallelFetch() {
    // 同时启动所有请求
    const userPromise = fetch('/api/user');
    const postsPromise = fetch('/api/posts');
    const commentsPromise = fetch('/api/comments');
    
    // 等待所有请求完成
    const [user, posts, comments] = await Promise.all([
        userPromise, 
        postsPromise, 
        commentsPromise
    ]);
    
    return { user, posts, comments };
}

错误处理模式

// 方法1: try/catch
async function withTryCatch() {
    try {
        const result = await asyncOperation();
        return result;
    } catch (error) {
        console.error('操作失败:', error);
        return null;
    }
}

// 方法2: 使用catch方法
async function withCatchMethod() {
    const result = await asyncOperation().catch(error => {
        console.error('操作失败:', error);
        return null;
    });
    
    return result;
}

// 方法3: 工具函数包装
function asyncHandler(promise) {
    return promise
        .then(data => [data, null])
        .catch(error => [null, error]);
}

// 使用工具函数
async function withHandler() {
    const [data, error] = await asyncHandler(asyncOperation());
    
    if (error) {
        console.error('操作失败:', error);
        return null;
    }
    
    return data;
}

综合项目实战:天气查询应用

下面我们将使用现代JavaScript异步编程技术构建一个完整的天气查询应用。

HTML结构

<div class="weather-app">
    <h1>城市天气查询</h1>
    <div class="search-container">
        <input type="text" id="city-input" placeholder="输入城市名称">
        <button id="search-btn">查询</button>
    </div>
    <div id="loading" class="hidden">加载中...</div>
    <div id="weather-result" class="hidden">
        <h2 id="city-name"></h2>
        <div id="weather-info"></div>
        <div id="error-message" class="hidden"></div>
    </div>
</div>

JavaScript实现

// 模拟API函数
const weatherAPI = {
    // 获取城市天气数据
    async getWeatherData(city) {
        // 模拟API调用延迟
        await new Promise(resolve => setTimeout(resolve, 800));
        
        // 模拟API响应数据
        const mockData = {
            '北京': { temperature: 22, condition: '晴', humidity: 45 },
            '上海': { temperature: 25, condition: '多云', humidity: 60 },
            '广州': { temperature: 28, condition: '小雨', humidity: 75 },
            '深圳': { temperature: 27, condition: '阴', humidity: 70 }
        };
        
        if (mockData[city]) {
            return mockData[city];
        } else {
            throw new Error(`找不到城市 "${city}" 的天气数据`);
        }
    },
    
    // 搜索城市建议
    async searchCities(query) {
        await new Promise(resolve => setTimeout(resolve, 300));
        
        const allCities = ['北京', '上海', '广州', '深圳', '杭州', '南京', '成都', '重庆'];
        return allCities.filter(city => 
            city.toLowerCase().includes(query.toLowerCase())
        );
    }
};

// 天气应用类
class WeatherApp {
    constructor() {
        this.cityInput = document.getElementById('city-input');
        this.searchBtn = document.getElementById('search-btn');
        this.loadingElem = document.getElementById('loading');
        this.weatherResult = document.getElementById('weather-result');
        this.cityNameElem = document.getElementById('city-name');
        this.weatherInfoElem = document.getElementById('weather-info');
        this.errorMessageElem = document.getElementById('error-message');
        
        this.init();
    }
    
    init() {
        this.searchBtn.addEventListener('click', () => this.searchWeather());
        this.cityInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') this.searchWeather();
        });
        
        // 添加输入建议功能
        this.cityInput.addEventListener('input', this.debounce(() => {
            this.showSuggestions();
        }, 300));
    }
    
    // 防抖函数
    debounce(func, delay) {
        let timeoutId;
        return function(...args) {
            clearTimeout(timeoutId);
            timeoutId = setTimeout(() => func.apply(this, args), delay);
        };
    }
    
    // 显示搜索建议
    async showSuggestions() {
        const query = this.cityInput.value.trim();
        if (query.length  {
        setTimeout(() => {
            reject(new Error(`操作超时 (${timeoutMs}ms)`));
        }, timeoutMs);
    });
    
    return Promise.race([promise, timeoutPromise]);
}

// 带重试的异步操作
async function withRetry(operation, maxRetries = 3, delayMs = 1000) {
    for (let attempt = 1; attempt  setTimeout(resolve, delayMs));
            
            // 指数退避策略
            delayMs *= 2;
        }
    }
}

// 初始化应用
document.addEventListener('DOMContentLoaded', () => {
    const app = new WeatherApp();
    
    // 使用高级异步功能示例
    const fetchWithTimeout = () => withTimeout(
        weatherAPI.getWeatherData('北京'), 
        2000
    );
    
    const fetchWithRetry = () => withRetry(
        () => weatherAPI.getWeatherData('上海'),
        3
    );
    
    // 测试高级功能
    fetchWithTimeout()
        .then(data => console.log('带超时的请求成功:', data))
        .catch(error => console.error('带超时的请求失败:', error));
        
    fetchWithRetry()
        .then(data => console.log('带重试的请求成功:', data))
        .catch(error => console.error('带重试的请求失败:', error));
});

项目总结与最佳实践

  • 使用async/await让异步代码更清晰易读
  • 合理使用Promise.all进行并行操作提高性能
  • 实现错误处理机制增强应用健壮性
  • 使用防抖技术优化用户输入处理
  • 实现超时和重试机制处理不可靠的网络请求

// 这里可以添加一些简单的样式和交互功能
document.addEventListener(‘DOMContentLoaded’, function() {
// 为导航菜单添加交互效果
const navItems = document.querySelectorAll(‘nav ul li a’);
navItems.forEach(item => {
item.addEventListener(‘click’, function(e) {
e.preventDefault();
const targetId = this.getAttribute(‘href’);
const targetElement = document.querySelector(targetId);

if (targetElement) {
targetElement.scrollIntoView({
behavior: ‘smooth’
});
}
});
});

// 为代码块添加复制功能
const codeBlocks = document.querySelectorAll(‘pre’);
codeBlocks.forEach(block => {
block.addEventListener(‘click’, function() {
const text = this.textContent;
navigator.clipboard.writeText(text).then(() => {
const originalText = this.textContent;
this.textContent = ‘代码已复制!’;

setTimeout(() => {
this.textContent = originalText;
}, 1500);
});
});
});
});

JavaScript异步编程完全指南:从Promise到Async/Await实战 | JS高级教程
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 javascript JavaScript异步编程完全指南:从Promise到Async/Await实战 | JS高级教程 https://www.taomawang.com/web/javascript/1073.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务