第 10 课:小白进阶必看!JavaScript 从回调地狱到 Async/Await 的异步编程革命

努力到无能为力,拼搏到感动自己!

当你面对层层嵌套的回调、无法定位的异步错误时,是否想过: 为什么处理异步这么痛苦?本节课将带你穿越时空,经历 JS 异步编程的进化史,最后用 Async/Await 开发「天气查询工具」,体验丝滑的异步代码!

一、异步编程进化三部曲

1. 回调地狱(Callback Hell)

getUser(userId, function(user) {
  getOrders(user.id, function(orders) {
    getItems(orders[0].id, function(items) {
      render(items, function() {
        // 更多嵌套...
      });
    });
  });
});

痛点:

  • 金字塔缩进,可读性差
  • 错误处理分散,难以维护
  • 无法并行/顺序控制

2. 蒸汽时代:Promise 链式调用

getUser(userId)
.then(user => getOrders(user.id))
.then(orders => getItems(orders[0].id))
.then(render)
.catch(error => console.error("全局捕获:", error));

优势:

  • 链式结构,线性表达异步流程
  • 统一错误处理
  • 支持 Promise.all 等高级操作

3. 电气时代:Async/Await 终极方案

async function loadData() {
try {
const user = await getUser(userId);
const orders = await getOrders(user.id);
const items = await getItems(orders[0].id);
await render(items);
} catch (error) {
console.error("加载失败:", error);
}
}

优势:

  • 同步写法写异步,心智负担低
  • 配合 try/catch 实现优雅错误处理
  • 代码可调试性大幅提升

二、Promise 核心机制精讲

1. 三种状态与链式传递

状态: pending → fulfilled/rejected(不可逆)

链式原理: .then 返回新 Promise,可连续调用

2. 创建 Promise

function fetchData(url) {
  return new Promise((resolve, reject) => {
    axios.get(url)
      .then(res => resolve(res.data))
      .catch(err => reject(err));
  });
}

3. 并发控制

// 并行请求,全部成功
Promise.all([api1, api2, api3])
  .then(([res1, res2, res3]) => {});
// 竞速模式,首个成功即返回
Promise.race([api1, api2])
  .then(firstResult => {});

三、Async/Await 实战技巧

1. 错误处理标准化

// 封装通用错误处理
async function safeRequest(url) {
  try {
    const res = await fetch(url);
    return await res.json();
  } catch (error) {
    reportError(error);  // 统一上报日志
    throw new Error("请求失败,请重试");
  }
}

2. 并行请求优化

// 顺序请求(慢)
const user = await getUser();
const orders = await getOrders(user.id);

// 并行请求(快)
const [user, orders] = await Promise.all([
  getUser(),
  getOrders(user.id)  // 假设已知user.id
]);

3. 突破循环限制

// for循环中使用await
async function processArray(array) {
  for (const item of array) {
    await processItem(item);  // 顺序执行
  }
}

// 并行处理所有元素
await Promise.all(array.map(item => processItem(item)));

四、实战:天气查询小工具

1. 功能需求

  • 输入城市名获取实时天气
  • 显示温度、天气图标、风速
  • 错误提示(如城市不存在)

2. 完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/qweather-icons@1.3.0/font/qweather-icons.css">
    <title>天气查询</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #f0f8ff;
            margin: 0;
            padding: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            color: #333;
        }

        h1 {
            font-size: 2.5em;
            margin-bottom: 20px;
            color: #4CAF50;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
        }

        .search-container {
            display: flex;
            align-items: center;
            margin-bottom: 20px;
        }

        input[type="text"] {
            padding: 15px;
            font-size: 18px;
            border: 2px solid #ccc;
            border-radius: 10px;
            width: 240px;
            margin-right: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        button {
            padding: 15px 30px;
            font-size: 18px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 10px;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            transition: background-color 0.3s ease;
        }

        button:hover {
            background-color: #45a049;
        }

        .weather-card {
            background: linear-gradient(135deg, #64b6ac, #5dbcd2);
            color: white;
            padding: 20px;
            border-radius: 15px;
            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
            text-align: left;
            margin-top: 20px;
            width: 350px;
            animation: fadeIn 0.5s ease;

        }

        .weather-card i {
            font-size: 30px;
            margin-left: 10px;
        }

        .weather-card p {
            margin: 5px 0;
            font-size: 1.2em;
            text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
        }

        .error {
            color: red;
            font-size: 1.2em;
            margin-top: 20px;
            text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
            }

            to {
                opacity: 1;
            }
        }
    </style>
</head>

<body>
    <h1>天气查询</h1>
    <div class="search-container">
        <input type="text" id="cityInput" placeholder="输入城市id(例如:北京-101010100)">
        <button onclick="getWeather()">查询</button>
    </div>
    <div id="weatherResult"></div>

    <script>
        const API_KEY = '772e2fb170e64febb119c74c71d65fd1';  // 和风天气API Key
        const BASE_URL = 'https://devapi.qweather.com/v7/weather/now';  // 和风天气API的基础URL

        async function getWeather() {
            const city = document.getElementById('cityInput').value; //101010100 北京
            if (!city) return;
            try {
                const url = `${BASE_URL}?location=${city}&key=${API_KEY}`
                const response = await fetch(url);
                const data = await response.json();

                if (data.code !== "200") throw new Error(data.message);

                displayWeather({
                    temp: data.now.temp,
                    icon: data.now.icon,
                    text: data.now.text,
                    wind: data.now.windDir,
                    humidity: data.now.humidity,
                    pressure: data.now.pressure
                });
            } catch (error) {
                showError(error.message);
            }
        }

        function displayWeather({ temp, icon, text, wind, humidity, pressure }) {
            const html = `
                <div class="weather-card">
                <i class="qi-${icon}"></i>
                <p>温度:${temp}℃</p>
                <p>天气:${text}</p>
                <p>风向:${wind}</p>
                <p>湿度:${humidity}%</p>
                <p>气压:${pressure} hPa</p>
                </div>
            `;
            document.getElementById('weatherResult').innerHTML = html;
        }

        function showError(msg) {
            document.getElementById('weatherResult').innerHTML =
                `<p class="error">${msg}</p>`;
        }
    </script>
</body>

</html>

3. 代码亮点

  • 异步流程清晰:用 Async/Await 替代回调嵌套
  • 解构赋值优化:直接提取嵌套数据
  • 错误处理集中:try/catch 捕获所有异常

下节预告

第 11 课:小白进阶必看!JavaScript 模块化与工程化——从脚本到项目

  • ES Modules 标准详解
  • Webpack 核心配置
  • 实战:将天气工具工程化

回复【JS】获取本课源码+工具包!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值