小熊奶糖(BearCandy)
小熊奶糖(BearCandy)
发布于 2025-10-05 / 3 阅读
0
0

Fetch API 深度解析笔记

Fetch API 深度解析笔记

1. 基础用法与常见错误

错误示例

fetch(url)
.then(data => {
    console.log(data.json().status); // ❌ 错误!
    // data.json() 返回 Promise,不能直接访问
})

正确用法

fetch(url)
.then(response => response.json()) // 返回 Promise
.then(data => {
    console.log(data.status); // ✅ 正确访问数据
})

2. 为什么需要两层 Promise?

设计原理

// 第一层:网络传输完成(收到响应头)
fetch(url).then(response => {
    // response 是 Response 对象(未拆的包裹)
    console.log(response.status);  // 直接访问 HTTP 状态码
    console.log(response.headers); // 直接访问响应头
  
    return response.json(); // 返回新的 Promise
})

// 第二层:数据解析完成(收到响应体)
.then(data => {
    // data 是解析后的 JavaScript 对象(拆开的包裹)
    console.log(data); // 访问实际数据
})

底层机制

// 伪代码展示 Fetch 内部工作原理
function fetch(url) {
    return new Promise(resolve => {
        // 阶段1:收到响应头
        onHeadersReceived((headers, status) => {
            const response = new Response({
                status: status,
                headers: headers,
                body: stream // 响应体还是数据流
            });
            resolve(response); // 第一层 Promise 完成
        });
    });
}

Response.prototype.json = function() {
    return new Promise(resolve => {
        // 阶段2:从流中读取并解析数据
        readStream(this.body, completeText => {
            const parsedData = JSON.parse(completeText);
            resolve(parsedData); // 第二层 Promise 完成
        });
    });
};

3. 多个 .then() 嵌套的情况

基本链式调用

fetch(url)
.then(response => {
    console.log("第1层:网络响应完成");
    return response.json();
})
.then(data => {
    console.log("第2层:JSON解析完成");
    return processData(data); // 返回新的 Promise
})
.then(processedData => {
    console.log("第3层:数据处理完成");
    return saveToDatabase(processedData);
})
.then(result => {
    console.log("第4层:数据库保存完成");
})
.catch(error => {
    console.error("错误处理:", error);
});

复杂数据处理场景

fetch('/api/users')
.then(response => {
    // 第一层:检查响应状态
    if (!response.ok) throw new Error('HTTP错误');
    console.log("HTTP状态:", response.status);
    return response.json();
})
.then(users => {
    // 第二层:处理用户数据
    console.log("用户数量:", users.length);
  
    // 获取第一个用户的详细信息
    return fetch(`/api/users/${users[0].id}`);
})
.then(response => {
    // 第三层:第二个请求的响应
    return response.json();
})
.then(userDetail => {
    // 第四层:处理用户详情
    console.log("用户详情:", userDetail);
  
    // 获取用户的订单
    return fetch(`/api/users/${userDetail.id}/orders`);
})
.then(response => response.json())
.then(orders => {
    // 第五层:处理订单数据
    console.log("订单数量:", orders.length);
})
.catch(error => {
    // 统一错误处理
    console.error("请求失败:", error);
});

并行处理多个请求

fetch('/api/users')
.then(response => response.json())
.then(users => {
    // 并行发起多个请求
    const promises = users.map(user => 
        fetch(`/api/users/${user.id}/profile`)
        .then(response => response.json())
    );
  
    return Promise.all(promises);
})
.then(profiles => {
    // 所有用户资料获取完成
    console.log("所有用户资料:", profiles);
  
    // 继续处理...
    return processProfiles(profiles);
})
.then(finalResult => {
    console.log("最终结果:", finalResult);
});

4. 错误处理策略

分层错误处理

fetch(url)
.then(response => {
    if (!response.ok) {
        throw new Error(`HTTP错误: ${response.status}`);
    }
    return response.json();
})
.then(data => {
    if (data.error) {
        throw new Error(`业务错误: ${data.error}`);
    }
    return data;
})
.catch(networkError => {
    console.error("网络或解析错误:", networkError);
})
.then(data => {
    // 即使前面有错误,这里仍会执行(data可能是undefined)
    if (data) {
        // 安全地处理数据
    }
})
.finally(() => {
    // 无论成功失败都会执行
    console.log("请求完成");
});

5. 实际应用模式

封装通用请求函数

async function apiRequest(url, options = {}) {
    try {
        const response = await fetch(url, options);
  
        // 第一层:HTTP层面检查
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }
  
        const data = await response.json();
  
        // 第二层:业务层面检查
        if (data.code !== 0) {
            throw new Error(`业务错误: ${data.message}`);
        }
  
        return data;
    } catch (error) {
        console.error('API请求失败:', error);
        throw error;
    }
}

// 使用示例
apiRequest('/api/order')
.then(orderData => {
    // 直接拿到验证过的数据
    console.log(orderData);
})
.catch(error => {
    // 统一的错误处理
    console.error(error);
});

6. 关键要点总结

为什么需要多层 .then()

  1. 关注点分离

    • 第一层:网络通信状态
    • 第二层:数据解析结果
    • 后续层:业务逻辑处理
  2. 性能优化

    • 流式处理大数据
    • 提前错误检测
  3. 灵活性

    • 根据响应决定解析方式
    • 链式发起多个相关请求

设计哲学

// 每个 .then() 都应该:
// 1. 返回一个值 → 成为下一个 .then() 的参数
// 2. 返回一个 Promise → 等待该 Promise 完成
// 3. 抛出错误 → 跳转到 .catch()

fetch(url)
.then(A) // 处理阶段A
.then(B) // 处理阶段B  
.then(C) // 处理阶段C
.catch(D) // 错误处理
.finally(E) // 最终清理

这种设计让异步代码更加清晰、可维护,每个 .then() 都代表一个明确的处理阶段!

.then(response => {response.json()}) 为什么要大括号?

问题出在第一个 .then(response => {response.json()}) 中的箭头函数写法上。

问题分析

// 错误的写法
.then(response => {response.json()})

这种写法的问题是:

  • 箭头函数使用了大括号 {},但没有明确返回值
  • 在大括号中需要显式使用 return 语句
  • 实际上这个函数返回的是 undefined,而不是 Promise

解决方案

方案1:去掉大括号(推荐)

fetch('http://pk2.honeybear.top/province/selectOrderInfo', sc)
    .then(response => response.json())  // 去掉大括号,隐式返回
    .then(response => {
        // 你的处理逻辑
    })

方案2:显式使用 return

fetch('http://pk2.honeybear.top/province/selectOrderInfo', sc)
    .then(response => {
        return response.json()  // 显式返回
    })
    .then(response => {
        // 你的处理逻辑
    })

完整修正代码

fetch('http://pk2.honeybear.top/province/selectOrderInfo', sc)
    .then(response => response.json())  // 修正这里
    .then(response => {
        let sdsd = document.getElementById('dsfds')
        let sdsd1 = document.getElementById('sdfjslkdA')
        sdsd1.innerHTML = response.status
        let sdsd2 = document.getElementById('dfsf')
        let sdsd3 = document.getElementById('dssdsd')
        sdsd.style = "display:block"

        response.data.forEach(element => {
            console.log(element)
            const row = document.createElement('tr');
            row.innerHTML = `
                <td>${element.id}</td>
                <td>${element.consignee}</td>
                <td>${element.create_time}</td>
                <td>${element.province_name}</td>
                <td>${element.trade_body}</td>
            `;
            sdsd3.appendChild(row)
        });
    })

箭头函数语法说明

  • response => response.json() - 隐式返回
  • response => { return response.json() } - 显式返回
  • response => { response.json() } - 错误,返回 undefined

推荐使用第一种简洁的隐式返回写法。


评论