掌握异步编程,让网页与服务器实时交互🌐

学习目标

  • ✅ 理解同步与异步编程的区别
  • ✅ 掌握Promise和async/await的使用
  • ✅ 学会使用Fetch API获取网络数据
  • ✅ 能够处理JSON数据和错误情况

📖📖 教程概览

现代Web应用需要与服务器进行数据交互。本教程将带你学习JavaScript的异步编程,掌握如何从API获取数据并动态更新页面内容。

💻💻 第一步:理解异步编程

1.1 同步 vs 异步

同步代码:按顺序执行,会阻塞后续代码

console.log('开始');
const result = expensiveCalculation(); // 耗时操作
console.log('结束'); // 必须等待上一步完成

异步代码:不会阻塞,通过回调处理结果

console.log('开始');
setTimeout(() => {
    console.log('异步操作完成');
}, 1000);
console.log('结束'); // 立即执行,不等待定时器

1.2 常见的异步操作

  • 定时器:setTimeout, setInterval
  • 事件监听:click, keypress等
  • 网络请求:Ajax, Fetch API
  • 文件操作:读取本地文件

🛠🛠 第二步:Promise详解

2.1 什么是Promise?

Promise是处理异步操作的对象,代表一个未来才会完成的操作。

2.2 Promise的三种状态

  • pending:进行中
  • fulfilled:已成功
  • rejected:已失败

2.3 Promise基本用法

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

// 使用Promise
myPromise
    .then(result => {
        console.log(result); // 操作成功!
    })
    .catch(error => {
        console.error(error); // 操作失败!
    });

2.4 Promise链式调用

fetchData()
    .then(data => processData(data))
    .then(processedData => displayData(processedData))
    .catch(error => console.error('出错:', error))
    .finally(() => console.log('操作完成'));

🌟🌟 第三步:async/await语法糖

3.1 更简洁的异步写法

// 传统的Promise写法
function fetchUser() {
    return fetch('/api/user')
        .then(response => response.json())
        .then(user => console.log(user))
        .catch(error => console.error(error));
}

// async/await写法
async function fetchUser() {
    try {
        const response = await fetch('/api/user');
        const user = await response.json();
        console.log(user);
    } catch (error) {
        console.error(error);
    }
}

3.2 async/await规则

  • async函数总是返回Promise
  • await只能在async函数中使用
  • 使用try/catch处理错误

3.3 并行处理多个异步操作

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

// 并行执行(较快)
async function parallelFetch() {
    const [user, posts] = await Promise.all([
        fetch('/api/user'),
        fetch('/api/posts')
    ]);
    return { user, posts };
}

🎮🎮 第四步:Fetch API实战

4.1 基本的Fetch请求

// GET请求
fetch('https://api.example.com/data')
    .then(response => {
        if (!response.ok) {
            throw new Error('网络响应不正常');
        }
        return response.json();
    })
    .then(data => console.log(data))
    .catch(error => console.error('错误:', error));

// 使用async/await
async function getData() {
    try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) throw new Error('请求失败');
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('获取数据失败:', error);
    }
}

4.2 POST请求与请求配置

async function postData() {
    const userData = {
        name: '张三',
        email: 'zhangsan@example.com'
    };

    const response = await fetch('https://api.example.com/users', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(userData)
    });

    const result = await response.json();
    return result;
}

4.3 处理不同的响应类型

// JSON数据
const jsonData = await response.json();

// 文本数据
const textData = await response.text();

// Blob数据(如图片)
const blobData = await response.blob();

// 表单数据
const formData = await response.formData();

🚀🚀 第五步:完整实战项目

任务要求:

创建一个天气查询应用,功能包括:

  • 从公开API获取实时天气数据
  • 显示当前天气信息和未来预报
  • 处理加载状态和错误情况
  • 实现城市搜索功能

完整代码实现:

HTML结构
<div class="weather-app">
    <header>
        <h1>天气查询应用</h1>
        <div class="search-box">
            <input type="text" id="cityInput" placeholder="输入城市名称">
            <button id="searchBtn">查询</button>
        </div>
    </header>
    
    <main>
        <div id="loading" class="hidden">加载中...</div>
        <div id="error" class="hidden"></div>
        
        <div id="weatherInfo" class="hidden">
            <div class="current-weather">
                <h2 id="cityName"></h2>
                <div class="temperature">
                    <span id="currentTemp"></span>°C
                </div>
                <div id="weatherDesc"></div>
            </div>
            
            <div class="forecast">
                <h3>未来预报</h3>
                <div id="forecastList"></div>
            </div>
        </div>
    </main>
</div>
JavaScript代码
class WeatherApp {
    constructor() {
        this.apiKey = 'your_api_key_here'; // 需要申请真实API密钥
        this.baseURL = 'https://api.openweathermap.org/data/2.5';
        this.init();
    }

    init() {
        this.cityInput = document.getElementById('cityInput');
        this.searchBtn = document.getElementById('searchBtn');
        this.loading = document.getElementById('loading');
        this.error = document.getElementById('error');
        this.weatherInfo = document.getElementById('weatherInfo');

        this.searchBtn.addEventListener('click', () => this.searchWeather());
        this.cityInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') this.searchWeather();
        });

        // 默认加载北京天气
        this.getWeatherByCity('北京');
    }

    async searchWeather() {
        const city = this.cityInput.value.trim();
        if (!city) {
            this.showError('请输入城市名称');
            return;
        }
        await this.getWeatherByCity(city);
    }

    async getWeatherByCity(city) {
        this.showLoading();
        this.hideError();
        this.hideWeatherInfo();

        try {
            // 获取当前天气
            const currentWeather = await this.fetchWeather(city);
            // 获取预报数据
            const forecast = await this.fetchForecast(city);
            
            this.displayWeather(currentWeather, forecast);
        } catch (error) {
            this.showError('获取天气数据失败:' + error.message);
        } finally {
            this.hideLoading();
        }
    }

    async fetchWeather(city) {
        const response = await fetch(
            `${this.baseURL}/weather?q=${city}&units=metric&appid=${this.apiKey}&lang=zh_cn`
        );
        
        if (!response.ok) {
            throw new Error('城市未找到或网络错误');
        }
        
        return await response.json();
    }

    async fetchForecast(city) {
        const response = await fetch(
            `${this.baseURL}/forecast?q=${city}&units=metric&appid=${this.apiKey}&lang=zh_cn`
        );
        
        if (!response.ok) {
            throw new Error('获取预报数据失败');
        }
        
        return await response.json();
    }

    displayWeather(current, forecast) {
        // 更新当前天气信息
        document.getElementById('cityName').textContent = `${current.name}, ${current.sys.country}`;
        document.getElementById('currentTemp').textContent = Math.round(current.main.temp);
        document.getElementById('weatherDesc').textContent = current.weather[0].description;

        // 显示预报数据
        this.displayForecast(forecast);
        
        this.showWeatherInfo();
    }

    displayForecast(forecast) {
        const forecastList = document.getElementById('forecastList');
        forecastList.innerHTML = '';

        // 显示未来5天的预报(每8小时一个数据点,取每天中午的数据)
        for (let i = 0; i < forecast.list.length; i += 8) {
            const item = forecast.list[i];
            const date = new Date(item.dt * 1000);
            const dayElement = document.createElement('div');
            dayElement.className = 'forecast-item';
            dayElement.innerHTML = `
                
${date.getMonth() + 1}/${date.getDate()}
${Math.round(item.main.temp)}°C
${item.weather[0].description}
`; forecastList.appendChild(dayElement); } } // UI控制方法 showLoading() { this.loading.classList.remove('hidden'); } hideLoading() { this.loading.classList.add('hidden'); } showError(message) { this.error.textContent = message; this.error.classList.remove('hidden'); } hideError() { this.error.classList.add('hidden'); } showWeatherInfo() { this.weatherInfo.classList.remove('hidden'); } hideWeatherInfo() { this.weatherInfo.classList.add('hidden'); } } // 初始化应用 new WeatherApp();
CSS样式
.hidden { display: none; }
.weather-app { max-width: 600px; margin: 0 auto; padding: 20px; }
.search-box { display: flex; gap: 10px; margin: 20px 0; }
.current-weather { text-align: center; margin: 30px 0; }
.temperature { font-size: 3em; font-weight: bold; }
.forecast-item { 
    display: inline-block; 
    padding: 10px; 
    margin: 5px; 
    background: #f5f5f5; 
    border-radius: 5px; 
}

📚📚 小结与练习

本章重点回顾:

  • 异步编程避免阻塞,提升用户体验
  • Promise是现代异步编程的基础
  • async/await让异步代码更易读写
  • Fetch API是浏览器内置的HTTP客户端

练习任务:

  1. 实现完整的天气查询应用
  2. 尝试使用其他公开API(如新闻API、GitHub API)
  3. 为应用添加地理位置自动检测功能
  4. 实现数据缓存,减少API请求次数

💡💡 常见问题

Q:Fetch请求遇到CORS错误怎么办?
A:确保API支持CORS,或使用代理服务器、JSONP等解决方案

Q:如何调试异步代码?
A:使用console.log、浏览器调试工具的断点功能,或添加详细的错误处理

Q:Promise和async/await哪个更好?
A:async/await更易读,但Promise更基础。建议根据场景选择,或混合使用

👉👉 下一篇预告

在最后一篇中,我们将学习前端工程化初探,了解现代前端开发工具和流程,为学习框架打下基础!

分类: 💻前端修炼「从零到上线」Web开发新手村  标签: 异步JavaScriptPromiseFetch API数据获取

评论

暂无评论数据

暂无评论数据

目录