前端修炼篇⑤:异步JavaScript与数据获取 - 连接动态数据
掌握异步编程,让网页与服务器实时交互🌐
学习目标
- ✅ 理解同步与异步编程的区别
- ✅ 掌握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
函数总是返回Promiseawait
只能在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客户端
练习任务:
- 实现完整的天气查询应用
- 尝试使用其他公开API(如新闻API、GitHub API)
- 为应用添加地理位置自动检测功能
- 实现数据缓存,减少API请求次数
💡💡 常见问题
Q:Fetch请求遇到CORS错误怎么办?
A:确保API支持CORS,或使用代理服务器、JSONP等解决方案
Q:如何调试异步代码?
A:使用console.log、浏览器调试工具的断点功能,或添加详细的错误处理
Q:Promise和async/await哪个更好?
A:async/await更易读,但Promise更基础。建议根据场景选择,或混合使用
👉👉 下一篇预告
在最后一篇中,我们将学习前端工程化初探,了解现代前端开发工具和流程,为学习框架打下基础!
版权申明
本文系作者 @sgyyds 原创发布在孙哥博客站点。未经许可,禁止转载。
暂无评论数据