综合练习
用 Promise + fetch 重构一段回调代码。
40 分钟
综合练习
本课串联 javascript-core 与本 track 所学:用 Promise + fetch 重构一段回调风格代码,并加入错误处理、并发与模块划分。完成后可继续 nodejs-essentials 服务端实践或 react-essentials 界面集成。
学习目标
- 将嵌套回调改写为 Promise 链或 async/await
- 用
Promise.all并发请求多个 API - 按 ESM 拆分 api / ui 模块
需求说明
实现「课程 track 详情页」数据加载:先取 track 元信息,再并行取 lessons 列表与 roadmap 节点,最后合并展示。
起始代码(回调风格,待重构)
function loadTrackPage(slug, onDone, onError) {
fetch('/api/tracks/' + slug, function (err, trackRes) {
if (err) return onError(err);
parseJson(trackRes, function (err2, track) {
if (err2) return onError(err2);
fetch('/api/tracks/' + slug + '/lessons', function (err3, lessonsRes) {
if (err3) return onError(err3);
parseJson(lessonsRes, function (err4, lessons) {
if (err4) return onError(err4);
fetch('/api/roadmaps/javascript', function (err5, mapRes) {
if (err5) return onError(err5);
parseJson(mapRes, function (err6, roadmap) {
if (err6) return onError(err6);
onDone({ track, lessons, roadmap });
});
});
});
});
});
});
}
参考答案结构
// api/http.js
export async function getJson(url, options = {}) {
const res = await fetch(url, options);
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${url}`);
}
return res.json();
}
// api/trackPage.js
import { getJson } from './http.js';
export async function loadTrackPage(slug) {
const track = await getJson(`/api/tracks/${slug}`);
const [lessons, roadmap] = await Promise.all([
getJson(`/api/tracks/${slug}/lessons`),
getJson('/api/roadmaps/javascript'),
]);
return { track, lessons, roadmap };
}
// main.js
import { loadTrackPage } from './api/trackPage.js';
async function main() {
try {
const data = await loadTrackPage('javascript-advanced-essentials');
render(data);
} catch (e) {
showError(e.message);
}
}
加分项
// 超时 + 取消
export async function getJson(url, { signal, timeoutMs = 8000 } = {}) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
const combined = signal
? AbortSignal.any([signal, controller.signal])
: controller.signal;
try {
const res = await fetch(url, { signal: combined });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} finally {
clearTimeout(timer);
}
}