JavaScript基础课程十八、异步编程高级(async/await + 模块化)

本课聚焦前端异步编程终极方案async/await与ES6模块化,是异步编程的收尾与进阶内容。async/await依托Promise,用同步写法实现异步逻辑,彻底解决回调嵌套和链式调用繁琐问题,是当前项目主流异步写法。模块化则解决代码混乱、全局污染、复用性差的痛点,实现代码规范化拆分。课程通过基础异步、并行执行、模块化拆分三类案例,串联起完整的高级异步开发流程,帮助建立工程化思维。掌握本课内容,既能写出简洁健壮的异步代码,又能搭建规范的项目结构,适配主流前端开发规范,也是面试高频考点,为后续框架学习打下坚实底层基础。

一、课程学习目的

  1. 理解async/await的底层依托,掌握其作为Promise语法糖的核心特性,彻底告别回调嵌套。

  2. 熟练使用async/await编写同步风格的异步代码,实现异步任务的串行、并行执行。

  3. 掌握try/catch捕获async/await异常的规范写法,保障异步逻辑稳定性。

  4. 理解JS模块化的意义,学会ES6模块化(export/import)的基础用法,实现代码拆分与复用。

  5. 结合async/await与模块化,搭建规范的异步项目结构,为工程化开发奠定基础。

二、核心知识点讲解

1. async/await 基础认知

async/await是ES7推出的Promise语法糖,基于Promise实现,让异步代码写法完全同步化,可读性和可维护性远超原生Promise链式调用。

核心规则

  • async:修饰函数,标记该函数为异步函数,内部可使用await,函数默认返回Promise对象。

  • await:只能在async函数内使用,暂停代码执行,等待右侧Promise状态变更后再继续。

2. async/await 执行与异常处理

await会等待Promise返回结果,成功则直接获取resolve值,失败则抛出异常;必须配合try/catch捕获错误,避免程序崩溃。

3. ES6 模块化基础

模块化用于拆分代码、避免全局污染、实现功能复用,是前端工程化的基础。

核心语法

  • export:导出模块内的变量、函数、类,分为默认导出(export default)和按需导出。

  • import:导入其他模块的内容,配合script type="module"使用。

4. async/await 结合模块化

将异步请求、工具函数封装为独立模块,通过import引入,在async函数内调用await执行,实现逻辑解耦、代码复用。

三、示例程序

示例1:async/await 基础用法

javascript 复制代码
// 复用第15课Promise封装
function getWordInfo(word) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (word) resolve({ en: word, cn: '中文释义' });
      else reject('单词不能为空');
    }, 1000);
  });
}

// async修饰异步函数
async function fetchWord() {
  // try/catch捕获异常
  try {
    // await等待Promise结果,同步写法
    const res1 = await getWordInfo('apple');
    console.log('第一个单词:', res1);
    const res2 = await getWordInfo('banana');
    console.log('第二个单词:', res2);
  } catch (err) {
    // 捕获await抛出的错误
    console.error('异步失败:', err);
  }
}

// 调用异步函数
fetchWord();

示例2:async/await 并行执行

javascript 复制代码
async function fetchAllWord() {
  try {
    // Promise.all实现并行,await等待全部结果
    const results = await Promise.all([
      getWordInfo('apple'),
      getWordInfo('banana'),
      getWordInfo('orange')
    ]);
    console.log('全部单词:', results);
  } catch (err) {
    console.error('加载失败:', err);
  }
}

fetchAllWord();

示例3:ES6模块化拆分

javascript 复制代码
// wordModule.js 模块文件(导出)
// 异步函数封装
export async function getWord(word) {
  try {
    const res = await fetch(`/api/word?name=${word}`);
    return res.json();
  } catch (err) {
    throw err;
  }
}

// 默认导出
export default {
  getWord
};

// 主文件 index.js 导入
import { getWord } from './wordModule.js';

async function init() {
  const data = await getWord('grape');
  console.log(data);
}

init();

四、掌握技巧与方法

  1. await必须写在async函数内,禁止在全局或普通函数中单独使用。

  2. 所有await逻辑都要包裹try/catch,规范捕获异步异常,避免未处理错误。

  3. 无依赖的异步任务用Promise.all+await并行执行,提升加载效率。

  4. 模块化拆分遵循单一职责,异步逻辑、工具函数、业务逻辑分开封装。

  5. 浏览器中使用ES6模块化,script标签需添加type="module"属性。

  6. async函数返回的Promise可继续链式调用,灵活适配复杂场景。

五、课后作业

基础作业

  1. 用async/await改写第15课Promise链式调用代码,实现串行获取单词。

  2. 使用try/catch捕获async/await的异常,测试失败场景。

  3. 编写async函数,通过await+Promise.all并行加载3组数据。

进阶作业

  1. 将Promise异步函数拆分为独立模块,用import导入并通过await调用。

  2. 实现异步加载的加载状态、成功、失败三种页面反馈。

  3. 在async函数内实现异步任务超时中断逻辑。

实战作业

  1. 搭建模块化异步单词查询页面,拆分异步请求模块、渲染模块,用async/await处理异步逻辑,实现加载提示、错误兜底、数据展示全流程。

上一课:异步编程进阶(Promise)实战作业代码

代码功能说明

本实战代码围绕Promise核心知识点,采用英语单词加载场景,实现完整的异步处理流程。代码封装Promise异步函数,模拟网络请求延迟获取单词数据,支持单个单词查询、链式串行加载、多单词并行加载三大功能,搭配页面加载状态、成功渲染、错误提示交互,直观展示Promise三种状态流转、then/catch/finally用法,以及Promise.all并行执行逻辑。页面配备功能按钮,点击可分步演示各类异步场景,全程贴合第15课核心考点,对比回调函数优势,为async/await学习做好铺垫。

注意事项

  • Promise状态一旦从pending变为fulfilled/rejected,不可再次修改,resolve/reject仅调用一次。

  • 必须添加catch捕获异常,避免未处理的Promise错误导致页面逻辑中断。

  • 链式调用时,then内部需返回新Promise,才能实现异步串行执行。

  • Promise.all中任一任务失败,整体会进入catch,需做好单个任务异常兜底。

  • finally常用于关闭加载状态、清理定时器,无论成功失败都会执行。

  • 调试时打开浏览器控制台,观察Promise状态变化与执行时序,加深理解。

  • 禁止在Promise执行器内编写同步阻塞代码,保证异步特性。

完整实战代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>第15课 Promise实战 - 单词异步加载器</title>
    <style>
        .box {
            width: 750px;
            margin: 50px auto;
            padding: 30px;
            border: 1px solid #eee;
            border-radius: 10px;
            font-family: "Microsoft YaHei";
            box-shadow: 0 0 12px rgba(0,0,0,0.06);
        }
        .btn {
            padding: 10px 20px;
            margin: 10px 8px;
            background: #42b983;
            color: #fff;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: 0.3s;
        }
        .btn:hover {
            background: #359469;
        }
        .result {
            margin-top: 25px;
            padding: 20px;
            line-height: 2;
            border-top: 1px dashed #eee;
            min-height: 180px;
        }
        .item {
            margin: 8px 0;
            padding: 10px;
            background: #f9f9f9;
            border-radius: 5px;
        }
        .loading {
            color: #666;
        }
        .error {
            color: #f56c6c;
        }
    </style>
</head>
<body>
    <div class="box">
        <h2>Promise单词异步加载器</h2>
        <button class="btn" onclick="getSingleWord()">单个单词查询</button>
        <button class="btn" onclick="getSeriesWord()">串行链式加载</button>
        <button class="btn" onclick="getAllWord()">并行批量加载</button>
        <button class="btn" onclick="clearResult()">清空结果</button>
        <div class="result" id="result"></div>
    </div>

    <script>
        const resDom = document.getElementById("result");
        // 渲染函数
        function render(html, className = '') {
            resDom.innerHTML += `<div class="item ${className}">${html}</div>`;
        }
        // 清空结果
        function clearResult() {
            resDom.innerHTML = "";
        }

        // Promise封装:异步获取单词
        function fetchWord(word) {
            return new Promise((resolve, reject) => {
                render(`正在加载:${word}`, 'loading');
                // 模拟网络延迟
                setTimeout(() => {
                    if (word && typeof word === 'string') {
                        // 成功
                        resolve({ en: word, cn: '对应中文释义', status: 'success' });
                    } else {
                        // 失败
                        reject('单词参数无效,加载失败');
                    }
                }, 1500);
            });
        }

        // 1. 单个单词查询
        function getSingleWord() {
            fetchWord('apple')
                .then(res => {
                    render(`成功:${res.en} - ${res.cn}`);
                })
                .catch(err => {
                    render(`错误:${err}`, 'error');
                })
                .finally(() => {
                    render('单次查询任务结束');
                });
        }

        // 2. 链式串行加载
        function getSeriesWord() {
            fetchWord('apple')
                .then(res => {
                    render(`成功1:${res.en} - ${res.cn}`);
                    return fetchWord('banana');
                })
                .then(res => {
                    render(`成功2:${res.en} - ${res.cn}`);
                    return fetchWord('orange');
                })
                .then(res => {
                    render(`成功3:${res.en} - ${res.cn}`);
                })
                .catch(err => {
                    render(`错误:${err}`, 'error');
                });
        }

        // 3. 并行批量加载
        function getAllWord() {
            const taskList = [fetchWord('apple'), fetchWord('banana'), fetchWord('grape')];
            Promise.all(taskList)
                .then(results => {
                    results.forEach((res, index) => {
                        render(`批量成功${index+1}:${res.en} - ${res.cn}`);
                    });
                })
                .catch(err => {
                    render(`批量错误:${err}`, 'error');
                });
        }
    </script>
</body>
</html>

作业验收标准

  1. 所有功能按钮点击正常,无控制台报错,加载、成功、错误状态展示清晰。

  2. 单个查询、串行链式、并行批量逻辑执行准确,符合Promise执行规则。

  3. 异常捕获到位,错误提示友好,finally收尾逻辑正常执行。

  4. 代码规范、注释完整,贴合第15课Promise核心知识点,交互简洁直观。