深入理解现代 JavaScript 异步编程:从回调到 Async/Await

39 阅读2分钟

在现代前端开发中,异步编程是必不可少的技能。本文将带你深入了解 JavaScript 异步编程的演进历程,从传统的回调函数到现代的 async/await。

异步编程的演进

1. 回调函数时代

在 ES6 之前,JavaScript 主要使用回调函数处理异步操作:

// ES6 之前的回调函数方式
fs.readFile('./1.html', 'utf-8', (err, data) => {
    if (err) {
        console.log(err);
        return;
    }
    console.log(data);
})

这种方式虽然简单,但容易导致"回调地狱",代码可读性差且难以维护。

2. Promise 的诞生

ES6 引入了 Promise,为异步操作提供了更优雅的解决方案:

const p = new Promise((resolve, reject) => {
    fs.readFile('./1.html', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
            return;
        }
        resolve(data);
    })
})

p.then(data => {
    console.log(data);
}).catch(err => {
    console.log(err);
})

Promise 解决了回调地狱的问题,但链式调用的 .then() 仍然不够直观。

3. Async/Await 的革命

ES8 引入了 async/await,让异步代码看起来像同步代码:

const main = async () => {
    const html = await p;
    console.log(html);
}
main();

实战:使用 Fetch API 调用大模型

基础 Fetch 用法

// 传统 .then() 方式
fetch('https://api.github.com/users/shunwuyu/repos')
.then(res => {
    return res.json();
})
.then(data => {
    console.log(data);
})

Async/Await 改进版

// 使用 async/await
const main = async () => {
    const res = await fetch('https://api.github.com/users/shunwuyu/repos');
    const data = await res.json();
    console.log(data);
}

复杂请求:调用 DeepSeek API

// 请求配置
const endpoint = 'https://api.deepseek.com/chat/completions';
const headers = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY}`
}

const payload = {
    'model': 'deepseek-chat',
    'messages': [
        {
            'role': 'system',
            'content': 'You are a helpful assistant.'
        },
        {
            'role': 'user',
            'content': '你好 DeepSeek'
        }
    ]
}

// 使用 async/await 发送请求
async function fetchData() {
    try {
        const response = await fetch(endpoint, {
            method: 'POST',
            headers,
            body: JSON.stringify(payload)
        });

        const data = await response.json();
        document.getElementById('reply').textContent = data.choices[0].message.content;
    } catch (error) {
        console.error('Error fetching data:', error);
        document.getElementById('reply').textContent = '请求失败,请检查控制台错误';
    }
}

fetchData();

关键概念解析

Fetch API 响应处理

名称类型说明
resResponseHTTP 响应对象,包含状态码、头部等信息
res.json()Promise读取并解析 JSON 的方法
dataObject/Array解析后的 JavaScript 数据

为什么选择 POST 请求

  • 安全性:POST 不会将数据暴露在 URL 中
  • 数据容量:支持更大的请求体
  • 语义明确:适合创建资源的操作

环境变量管理

在前端项目中,敏感信息如 API Key 应该存储在环境变量中:

// 使用 Vite 环境变量
'Authorization': `Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY}`

最佳实践

  1. 错误处理:始终使用 try-catch 包装 await 调用
  2. 模块化:使用 type="module" 启用 ES6 模块功能
  3. 类型检查:确保请求头和请求体格式正确
  4. 安全考虑:API Key 等敏感信息不要硬编码在代码中

总结

从回调函数到 Promise,再到 async/await,JavaScript 的异步编程变得越来越简洁和直观。async/await 不仅让代码更易读,还大大降低了异步编程的复杂度。在现代前端开发中,掌握这些异步编程技术是必不可少的技能。

通过本文的示例,你可以看到如何使用现代 JavaScript 特性与 AI 大模型进行交互,这在当今的 AI 应用开发中是非常实用的技能。