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()
-
关注点分离:
- 第一层:网络通信状态
- 第二层:数据解析结果
- 后续层:业务逻辑处理
-
性能优化:
- 流式处理大数据
- 提前错误检测
-
灵活性:
- 根据响应决定解析方式
- 链式发起多个相关请求
设计哲学
// 每个 .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
推荐使用第一种简洁的隐式返回写法。