)
文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载本篇基于《现代 JavaScript 教程》中文仓库中 1-js/11-async/08-async-await/01-rewrite-async/task.md 的经典练习题展开把一个基于.then/.catch的loadJson(url)网络请求封装改写为async/await语法。读完本文你将掌握async函数返回值的规则、await的暂停与取值语义、throw与 promise rejection 的等价关系以及在非async顶层作用域中如何配合.catch消费异步结果 —— 这也是从 Promise 链平滑过渡到async/await的关键一步。一、练习背景源自 Promise 链一章的 loadJson 示例本题要求重写的代码出自教程的 Promise 链Promise chaining 一章。那一章用fetch封装了一个读取 JSON 的工具函数基本形态如下function loadJson(url) { return fetch(url) .then(response response.json()); }而练习题给出的完整版本在此基础上加入了状态码校验function loadJson(url) { return fetch(url) .then(response { if (response.status 200) { return response.json(); } else { throw new Error(response.status); } }); } loadJson(https://javascript.info/no-such-user.json) .catch(alert); // Error: 404这段代码的两个关键行为状态码分流fetch在服务器返回响应头时即 resolve并非等完整响应下载完response.status 200表示成功此时调用response.json()返回一个解析 JSON 的新 promise否则直接throw new Error(response.status)该异常会被 promise 机制捕获使返回的 promise 变为 rejected。错误透传调用方在顶层通过.catch(alert)捕获 rejection最终弹出Error: 404。任务要求不改变功能仅把.then链重写为async/await形式。二、参考解法async 函数 await 的逐步改写练习题的标准答案位于 01-rewrite-async/solution.md完整代码如下async function loadJson(url) { // (1) let response await fetch(url); // (2) if (response.status 200) { let json await response.json(); // (3) return json; } throw new Error(response.status); } loadJson(https://javascript.info/no-such-user.json) .catch(alert); // Error: 404 (4)改写要点逐条解析给函数加上async关键字注释 (1)。这是使用await的前提也保证函数无论内部逻辑如何返回值都会被包装成一个 promise。关于这一点教程正文 08-async-await/article.md 明确指出async确保函数总是返回一个 promise其他值会被自动包装进一个 resolved 的 promise。用await替换.then注释 (2)。await fetch(url)让 JavaScript 引擎挂起当前函数的执行直到fetch返回的 promise settle若成功表达式的值就是 resolve 的结果即response对象并直接赋值给let response。教程强调这种“暂停”不耗费 CPU引擎可以同时处理其他脚本与事件。对response.json()同样使用await注释 (3)拿到解析后的 JSON 对象作为函数返回值async函数会自动把该值包装成 resolved promise。错误处理交给外部的.catch注释 (4)。async函数内部throw new Error(response.status)会让loadJson(...)返回的 promise 变为 rejected与原来.then内部的throw行为完全等价因此loadJson(...).catch(alert)依旧弹出Error: 404。三、两个易混点能否直接return response.json()与内部 await解法注释特别提醒一个常见疑问能不能不await response.json()直接return response.json()答案是可以。因为async函数会自动把返回值即使是一个 promise展开并包装成最终 promise两种写法对外部调用者来说结果一致if (response.status 200) { return response.json(); // 不等待直接把 promise 返回 }区别在于直接return时函数在解析 JSON 完成之前就返回了此时必须由外部代码await这个 promise 才能真正拿到数据。在本例只做状态码校验中无关紧要因此标准解法选择了更直观的await写法让数据在函数内部就绪。作为良好实践在async函数内部应尽量await掉所有需要结果的 promise保证函数返回时数据已可用。第二个易混点是“非 async 顶层不能用 await”。解法注释 (4) 专门说明这里不能写await loadJson(...)因为调用语句位于所有async函数之外顶层await只有在现代浏览器的 module 作用域中才被允许。因此顶层的标准做法仍是.catch(alert)。这是async/await与promise.then/catch长期共存的根本原因教程在 08-async-await/article.md 的错误处理小节中也有同样的总结在语法上无法使用await的顶层通常需要追加.then/catch处理最终结果或错误。四、从示例看 await 的错误传播机制本题中throw new Error(response.status)之所以能被外层的.catch(alert)捕获底层依据是await与throw的等价关系。教程正文给出了这一对等价写法async function f() { await Promise.reject(new Error(Whoops!)); } // 等价于 async function f() { throw new Error(Whoops!); }即如果被await的 promise 被 rejectawait会像执行了throw一样抛出该错误。在真实场景中reject 可能延迟发生此时await抛出错误之前会有相应延时但传播机制不变。若希望就地处理错误可以使用常规的try..catch包装多行awaitasync function f() { try { let response await fetch(/no-user-here); let user await response.json(); } catch(err) { // 同时捕获 fetch 与 response.json() 的错误 alert(err); } }五、仓库同章进阶练习rethrow 循环中的 try..catch同目录下还有一道进阶练习 02-rewrite-async-2/task.md它把本题思想延伸到“请求 GitHub 用户信息直到输入合法为止”的重试场景原实现用.catch内递归调用demoGithubUser()实现重试。改用async/await后递归可以被while(true)循环取代class HttpError extends Error { constructor(response) { super(${response.status} for ${response.url}); this.name HttpError; this.response response; } } async function loadJson(url) { let response await fetch(url); if (response.status 200) { return response.json(); } else { throw new HttpError(response); } } async function demoGithubUser() { let user; while(true) { let name prompt(Enter a name?, iliakan); try { user await loadJson(https://api.github.com/users/${name}); break; // 没有 error退出循环 } catch(err) { if (err instanceof HttpError err.response.status 404) { alert(No such user, please reenter.); // 循环继续 } else { throw err; // 未知错误重新抛出 } } } alert(Full name: ${user.name}.); return user; } demoGithubUser();这段代码展示了async/await相对 Promise 链的一个显著优势try..catch能把异步错误当作同步异常处理配合while循环实现“重试直到成功”的流程代码是扁平的顺序结构而非回调嵌套或链式递归。完整解法见 02-rewrite-async-2/solution.md。六、延伸一题在普通函数中调用 async 函数同章的 03-async-from-regular/task.md 与本题互补考察“普通函数内如何拿到 async 函数的结果”。由于await不能出现在非async函数中正确做法是把async调用当作普通 promise用.then消费结果async function wait() { await new Promise(resolve setTimeout(resolve, 1000)); return 10; } function f() { // 1 秒后显示 10 wait().then(result alert(result)); } f();这也再次印证了本文的核心认知async/await不是对 promise 的替代而是建立在 promise 之上的语法糖。任何async函数调用都返回 promise因此在await不可用的作用域.then/.catch依然是标准接口。相关源码级示例可对照 03-promise-chaining 章节 中fetch链式调用与loadJson拆分的完整代码。七、小结本题沉淀的四个要点async声明函数必定返回 promise并允许函数体内使用awaitawait暂停函数直到 promise settle成功时返回结果失败时抛出错误等价于throwasync函数内可return response.json()直接返回 promise但通常应await后再返回保证函数返回时数据已就绪顶层非async作用域无法使用await必须用.then/.catch消费结果或捕获错误现代浏览器 module 顶层除外。本练习完整代码与标准答案位于仓库 1-js/11-async/08-async-await/01-rewrite-async/ 目录含 task.md 与 solution.md可作为独立代码片段直接在浏览器控制台运行验证。赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐JavaScript.info 实战用 async/await 重写 Promise 链式 loadJson 请求JavaScript.info 实战用 async/await 重写 Promise 链式 loadJson 请求 本篇技术指南以 Modern JavaSc文档/教程前端JavaScript Promise 链改写为 async/await 实战以 loadJson 网络请求为例JavaScript Promise 链改写为 async/await 实战以 loadJson 网络请求为例 导读 本文将围绕《Modern JavaScr文档/教程前端Modern JavaScript Tutorial 实战用 async/await 重写 Promise 链式 rethrow 错误处理递归改循环Modern JavaScript Tutorial 实战用 async/await 重写 Promise 链式 rethrow 错误处理递归改循环文档/教程前端上一篇FFXVIFix开源工具动态帧率控制与超宽屏适配解决方案 | 最终幻想16玩家的画质增强指南下一篇License_Plate_Detection_Pytorch部署指南从Pytorch模型到生产环境的全流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考