[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-vue-3-yibuqingqiukongzhichuanxingyubingxingdeyouyashijian":3},{"id":4,"title":5,"slug":6,"summary":7,"content":8,"contentHtml":9,"wordCount":10,"readingTime":11,"categoryId":12,"tags":13,"coverImage":13,"thumbnail":13,"status":14,"isTop":15,"isRecommended":15,"allowComments":15,"password":13,"viewCount":16,"likeCount":15,"commentCount":15,"seoTitle":5,"seoKeywords":17,"seoDescription":18,"source":13,"sourceUrl":13,"publishTime":19},"2039999965848203266","Vue 3 异步请求控制：串行与并行的优雅实践","vue-3-yibuqingqiukongzhichuanxingyubingxingdeyouyashijian","在 Vue 3 的开发中，处理异步请求是家常便饭。我们经常遇到这样的场景：需要等待一个或多个 API 请求完成后，才能进行下一步的数据处理或页面渲染。如何清晰、高效地控制这些请求的执行顺序，是构建健壮应用的关键。 本文将探讨在 Vue 3 中控制异步请求的两种核心模式：**串行执行**和**并行执行**，并结合 `async\u002Fawait`、`Promise.all` 等现代 JavaScript 特性，提供清晰、可维护的解决方案。 #### 🎯 场景一：串行执行，处理请求依赖 当一个请求的执行依赖于前一个请求的返回结果时，我们必须确保它们按顺序执行。例如，先获取用户 ID，再根据 ID 获取用户的详细信息和订单列表。 在 Vue 3 的 ` 加载中...","在 Vue 3 的开发中，处理异步请求是家常便饭。我们经常遇到这样的场景：需要等待一个或多个 API 请求完成后，才能进行下一步的数据处理或页面渲染。如何清晰、高效地控制这些请求的执行顺序，是构建健壮应用的关键。\n\n本文将探讨在 Vue 3 中控制异步请求的两种核心模式：**串行执行**和**并行执行**，并结合 `async\u002Fawait`、`Promise.all` 等现代 JavaScript 特性，提供清晰、可维护的解决方案。\n\n#### 🎯 场景一：串行执行，处理请求依赖\n\n当一个请求的执行依赖于前一个请求的返回结果时，我们必须确保它们按顺序执行。例如，先获取用户 ID，再根据 ID 获取用户的详细信息和订单列表。\n\n在 Vue 3 的 `\u003Cscript setup>` 语法中，我们可以利用 `async\u002Fawait` 语法糖，让异步代码的书写和阅读体验如同同步代码一般流畅。\n\n**核心思路：**\n在一个 `async` 函数中，使用 `await` 关键字。`await` 会暂停函数的执行，直到其后的 Promise 对象完成（resolve 或 reject），然后恢复执行并返回 Promise 的结果。\n\n**代码示例：**\n\n```javascript\n\u002F\u002F composables\u002FuseUserFlow.js\nimport { ref } from 'vue'\n\nexport function useUserFlow() {\n  const userData = ref(null)\n  const ordersData = ref(null)\n  const statsData = ref(null)\n  const loading = ref(false)\n  const error = ref(null)\n\n  const handleUserFlow = async () => {\n    loading.value = true\n    error.value = null\n    try {\n      \u002F\u002F 1. 等待第一个请求完成\n      const userResponse = await fetch('\u002Fapi\u002Fuser')\n      if (!userResponse.ok) throw new Error('获取用户失败')\n      userData.value = await userResponse.json()\n      console.log('1. 用户数据获取成功:', userData.value)\n\n      \u002F\u002F 2. 使用上一步的结果作为参数，等待第二个请求完成\n      const ordersResponse = await fetch(`\u002Fapi\u002Forders?userId=${userData.value.id}`)\n      if (!ordersResponse.ok) throw new Error('获取订单失败')\n      ordersData.value = await ordersResponse.json()\n      console.log('2. 订单数据获取成功:', ordersData.value)\n\n      \u002F\u002F 3. 等待第三个请求完成\n      const statsResponse = await fetch('\u002Fapi\u002Fstats')\n      if (!statsResponse.ok) throw new Error('获取统计失败')\n      statsData.value = await statsResponse.json()\n      console.log('3. 统计数据获取成功:', statsData.value)\n\n      \u002F\u002F 4. 所有请求都按顺序完成后，执行后续处理\n      console.log('所有请求完成，开始处理最终逻辑')\n      \u002F\u002F processFinalData(userData.value, ordersData.value, statsData.value)\n\n    } catch (err) {\n      \u002F\u002F 任何一个请求失败，都会跳转到这里\n      error.value = err.message\n      console.error('请求流程中出现错误:', err)\n    } finally {\n      loading.value = false\n    }\n  }\n\n  return {\n    userData,\n    ordersData,\n    statsData,\n    loading,\n    error,\n    handleUserFlow\n  }\n}\n```\n\n在组件中使用这个组合式函数：\n\n```vue\n\u003C!-- UserProfile.vue -->\n\u003Cscript setup>\nimport { onMounted } from 'vue'\nimport { useUserFlow } from '@\u002Fcomposables\u002FuseUserFlow'\n\nconst { loading, error, handleUserFlow } = useUserFlow()\n\n\u002F\u002F 在组件挂载时触发串行请求\nonMounted(() => {\n  handleUserFlow()\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"loading\">加载中...\u003C\u002Fdiv>\n  \u003Cdiv v-else-if=\"error\">发生错误: {{ error }}\u003C\u002Fdiv>\n  \u003Cdiv v-else>\n    \u003C!-- 渲染数据 -->\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\n#### 🚀 场景二：并行执行，提升加载效率\n\n当需要同时发起多个没有依赖关系的请求，并等待它们**全部**完成后再进行下一步时，使用 `Promise.all` 是最佳选择。这能显著提升效率，因为请求是同时发出的，总耗时约等于最慢的那个请求的时间，而不是所有请求耗时的总和。\n\n`Promise.all` 接收一个 Promise 数组，并返回一个新的 Promise。只有当数组中所有的 Promise 都成功 resolve 时，它才会 resolve；只要有一个 Promise reject，它就会立即 reject。\n\n**代码示例：**\n\n```javascript\n\u002F\u002F composables\u002FuseDashboard.js\nimport { ref } from 'vue'\n\nexport function useDashboard() {\n  const userInfo = ref(null)\n  const productList = ref(null)\n  const orderStats = ref(null)\n  const loading = ref(false)\n  const error = ref(null)\n\n  const loadDashboardData = async () => {\n    loading.value = true\n    error.value = null\n    try {\n      \u002F\u002F 同时发起三个独立的请求\n      const [userRes, productRes, orderRes] = await Promise.all([\n        fetch('\u002Fapi\u002Fuser\u002Finfo'),\n        fetch('\u002Fapi\u002Fproduct\u002Flist'),\n        fetch('\u002Fapi\u002Forder\u002Fstatistics')\n      ])\n\n      \u002F\u002F 等待所有请求都成功后，统一处理结果\n      userInfo.value = await userRes.json()\n      productList.value = await productRes.json()\n      orderStats.value = await orderRes.json()\n\n      console.log('所有独立请求完成，开始处理仪表盘数据')\n      \u002F\u002F updateDashboard(userInfo.value, productList.value, orderStats.value)\n\n    } catch (err) {\n      \u002F\u002F 只要有一个请求失败，Promise.all 就会立即失败并进入 catch\n      error.value = err.message\n      console.error('加载仪表盘数据失败:', err)\n    } finally {\n      loading.value = false\n    }\n  }\n\n  return {\n    userInfo,\n    productList,\n    orderStats,\n    loading,\n    error,\n    loadDashboardData\n  }\n}\n```\n\n#### 💡 进阶：处理部分失败的情况\n\n`Promise.all` 的“一损俱损”特性在某些场景下可能不适用。例如，加载一个页面时，关键数据必须成功，但一些可选的推荐数据即使失败也不应影响页面的正常展示。这时，`Promise.allSettled` 就派上用场了。\n\n`Promise.allSettled` 会等待所有传入的 Promise 完成（无论成功或失败），并返回一个数组，其中包含每个 Promise 的结果对象（包含 `status`、`value` 或 `reason`）。\n\n**代码示例：**\n\n```javascript\nasync function robustDataLoad() {\n  const promises = [\n    fetch('\u002Fapi\u002Fcritical-data'), \u002F\u002F 关键数据\n    fetch('\u002Fapi\u002Foptional-data-1'), \u002F\u002F 可选数据\n    fetch('\u002Fapi\u002Foptional-data-2')  \u002F\u002F 可选数据\n  ]\n\n  const results = await Promise.allSettled(promises)\n\n  const criticalData = results[0].status === 'fulfilled' ? await results[0].value.json() : null\n  const optionalData1 = results[1].status === 'fulfilled' ? await results[1].value.json() : null\n  const optionalData2 = results[2].status === 'fulfilled' ? await results[2].value.json() : null\n\n  \u002F\u002F 即使可选数据加载失败，关键数据仍然可用\n  if (criticalData) {\n    renderPage(criticalData, optionalData1, optionalData2)\n  } else {\n    showError('关键数据加载失败')\n  }\n}\n```\n\n#### 📌 总结与最佳实践\n\n| 执行方式 | 核心语法 | 适用场景 |\n| :--- | :--- | :--- |\n| **串行执行** | `async\u002Fawait` | 请求之间有强依赖关系，后一个请求需要前一个的结果。 |\n| **并行执行** | `Promise.all` | 多个请求相互独立，需要等待所有请求都成功后再统一处理。 |\n| **容错并行** | `Promise.allSettled` | 多个请求相互独立，即使部分失败也需要获取成功的结果。 |\n\n**最佳实践：**\n\n1.  **始终使用 `try...catch`**：异步操作随时可能失败，使用 `try...catch` 块可以优雅地捕获和处理错误，避免应用崩溃。\n2.  **管理加载状态**：为用户提供清晰的反馈，例如在请求开始时显示加载指示器，请求结束后隐藏。\n3.  **封装可复用的逻辑**：将异步请求逻辑封装在组合式函数（Composables）中，可以提高代码的复用性和可维护性。\n4.  **请求取消**：在组件卸载时，考虑使用 `AbortController` 取消未完成的请求，防止内存泄漏和状态更新错误。\n\n","\u003Cp data-line=\"0\">在 Vue 3 的开发中，处理异步请求是家常便饭。我们经常遇到这样的场景：需要等待一个或多个 API 请求完成后，才能进行下一步的数据处理或页面渲染。如何清晰、高效地控制这些请求的执行顺序，是构建健壮应用的关键。\u003C\u002Fp>\n\u003Cp data-line=\"2\">本文将探讨在 Vue 3 中控制异步请求的两种核心模式：\u003Cstrong>串行执行\u003C\u002Fstrong>和\u003Cstrong>并行执行\u003C\u002Fstrong>，并结合 \u003Ccode>async\u002Fawait\u003C\u002Fcode>、\u003Ccode>Promise.all\u003C\u002Fcode> 等现代 JavaScript 特性，提供清晰、可维护的解决方案。\u003C\u002Fp>\n\u003Ch4 data-line=\"4\" id=\"🎯 场景一：串行执行，处理请求依赖\">🎯 场景一：串行执行，处理请求依赖\u003C\u002Fh4>\n\u003Cp data-line=\"6\">当一个请求的执行依赖于前一个请求的返回结果时，我们必须确保它们按顺序执行。例如，先获取用户 ID，再根据 ID 获取用户的详细信息和订单列表。\u003C\u002Fp>\n\u003Cp data-line=\"8\">在 Vue 3 的 \u003Ccode>&lt;script setup&gt;\u003C\u002Fcode> 语法中，我们可以利用 \u003Ccode>async\u002Fawait\u003C\u002Fcode> 语法糖，让异步代码的书写和阅读体验如同同步代码一般流畅。\u003C\u002Fp>\n\u003Cp data-line=\"10\">\u003Cstrong>核心思路：\u003C\u002Fstrong>\u003Cbr>\n在一个 \u003Ccode>async\u003C\u002Fcode> 函数中，使用 \u003Ccode>await\u003C\u002Fcode> 关键字。\u003Ccode>await\u003C\u002Fcode> 会暂停函数的执行，直到其后的 Promise 对象完成（resolve 或 reject），然后恢复执行并返回 Promise 的结果。\u003C\u002Fp>\n\u003Cp data-line=\"13\">\u003Cstrong>代码示例：\u003C\u002Fstrong>\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"15\" class=\"md-editor-code\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">javascript\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-javascript\" language=javascript>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-comment\">\u002F\u002F composables\u002FuseUserFlow.js\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> { ref } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;vue&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">useUserFlow\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> userData = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> ordersData = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> statsData = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> loading = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> error = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">handleUserFlow\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">async\u003C\u002Fspan> (\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) =&gt; {\n    loading.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\n    error.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">try\u003C\u002Fspan> {\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 1. 等待第一个请求完成\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> userResponse = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Fuser&#x27;\u003C\u002Fspan>)\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!userResponse.\u003Cspan class=\"hljs-property\">ok\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Error\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;获取用户失败&#x27;\u003C\u002Fspan>)\n      userData.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> userResponse.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>()\n      \u003Cspan class=\"hljs-variable language_\">console\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">log\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;1. 用户数据获取成功:&#x27;\u003C\u002Fspan>, userData.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)\n\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 2. 使用上一步的结果作为参数，等待第二个请求完成\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> ordersResponse = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">`\u002Fapi\u002Forders?userId=\u003Cspan class=\"hljs-subst\">${userData.value.id}\u003C\u002Fspan>`\u003C\u002Fspan>)\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!ordersResponse.\u003Cspan class=\"hljs-property\">ok\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Error\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;获取订单失败&#x27;\u003C\u002Fspan>)\n      ordersData.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> ordersResponse.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>()\n      \u003Cspan class=\"hljs-variable language_\">console\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">log\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;2. 订单数据获取成功:&#x27;\u003C\u002Fspan>, ordersData.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)\n\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 3. 等待第三个请求完成\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> statsResponse = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Fstats&#x27;\u003C\u002Fspan>)\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!statsResponse.\u003Cspan class=\"hljs-property\">ok\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Error\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;获取统计失败&#x27;\u003C\u002Fspan>)\n      statsData.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> statsResponse.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>()\n      \u003Cspan class=\"hljs-variable language_\">console\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">log\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;3. 统计数据获取成功:&#x27;\u003C\u002Fspan>, statsData.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)\n\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 4. 所有请求都按顺序完成后，执行后续处理\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-variable language_\">console\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">log\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;所有请求完成，开始处理最终逻辑&#x27;\u003C\u002Fspan>)\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F processFinalData(userData.value, ordersData.value, statsData.value)\u003C\u002Fspan>\n\n    } \u003Cspan class=\"hljs-keyword\">catch\u003C\u002Fspan> (err) {\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 任何一个请求失败，都会跳转到这里\u003C\u002Fspan>\n      error.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = err.\u003Cspan class=\"hljs-property\">message\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-variable language_\">console\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">error\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;请求流程中出现错误:&#x27;\u003C\u002Fspan>, err)\n    } \u003Cspan class=\"hljs-keyword\">finally\u003C\u002Fspan> {\n      loading.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\n    }\n  }\n\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> {\n    userData,\n    ordersData,\n    statsData,\n    loading,\n    error,\n    handleUserFlow\n  }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"72\">在组件中使用这个组合式函数：\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"74\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">vue\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-vue\" language=vue>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"language-xml\">\u003Cspan class=\"hljs-comment\">&lt;!-- UserProfile.vue --&gt;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">script\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">setup\u003C\u002Fspan>&gt;\u003C\u002Fspan>\u003Cspan class=\"language-javascript\">\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> { onMounted } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;vue&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> { useUserFlow } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;@\u002Fcomposables\u002FuseUserFlow&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> { loading, error, handleUserFlow } = \u003Cspan class=\"hljs-title function_\">useUserFlow\u003C\u002Fspan>()\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F 在组件挂载时触发串行请求\u003C\u002Fspan>\n\u003Cspan class=\"hljs-title function_\">onMounted\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> {\n  \u003Cspan class=\"hljs-title function_\">handleUserFlow\u003C\u002Fspan>()\n})\n\u003C\u002Fspan>\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">script\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">template\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">v-if\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;loading&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>加载中...\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">v-else-if\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;error&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>发生错误: {{ error }}\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">v-else\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-comment\">&lt;!-- 渲染数据 --&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">template\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch4 data-line=\"97\" id=\"🚀 场景二：并行执行，提升加载效率\">🚀 场景二：并行执行，提升加载效率\u003C\u002Fh4>\n\u003Cp data-line=\"99\">当需要同时发起多个没有依赖关系的请求，并等待它们\u003Cstrong>全部\u003C\u002Fstrong>完成后再进行下一步时，使用 \u003Ccode>Promise.all\u003C\u002Fcode> 是最佳选择。这能显著提升效率，因为请求是同时发出的，总耗时约等于最慢的那个请求的时间，而不是所有请求耗时的总和。\u003C\u002Fp>\n\u003Cp data-line=\"101\">\u003Ccode>Promise.all\u003C\u002Fcode> 接收一个 Promise 数组，并返回一个新的 Promise。只有当数组中所有的 Promise 都成功 resolve 时，它才会 resolve；只要有一个 Promise reject，它就会立即 reject。\u003C\u002Fp>\n\u003Cp data-line=\"103\">\u003Cstrong>代码示例：\u003C\u002Fstrong>\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"105\" class=\"md-editor-code\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">javascript\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-javascript\" language=javascript>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-comment\">\u002F\u002F composables\u002FuseDashboard.js\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> { ref } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;vue&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">useDashboard\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> userInfo = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> productList = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> orderStats = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> loading = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> error = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">loadDashboardData\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">async\u003C\u002Fspan> (\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) =&gt; {\n    loading.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\n    error.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">try\u003C\u002Fspan> {\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 同时发起三个独立的请求\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [userRes, productRes, orderRes] = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Promise\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">all\u003C\u002Fspan>([\n        \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Fuser\u002Finfo&#x27;\u003C\u002Fspan>),\n        \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Fproduct\u002Flist&#x27;\u003C\u002Fspan>),\n        \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Forder\u002Fstatistics&#x27;\u003C\u002Fspan>)\n      ])\n\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 等待所有请求都成功后，统一处理结果\u003C\u002Fspan>\n      userInfo.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> userRes.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>()\n      productList.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> productRes.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>()\n      orderStats.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> orderRes.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>()\n\n      \u003Cspan class=\"hljs-variable language_\">console\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">log\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;所有独立请求完成，开始处理仪表盘数据&#x27;\u003C\u002Fspan>)\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F updateDashboard(userInfo.value, productList.value, orderStats.value)\u003C\u002Fspan>\n\n    } \u003Cspan class=\"hljs-keyword\">catch\u003C\u002Fspan> (err) {\n      \u003Cspan class=\"hljs-comment\">\u002F\u002F 只要有一个请求失败，Promise.all 就会立即失败并进入 catch\u003C\u002Fspan>\n      error.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = err.\u003Cspan class=\"hljs-property\">message\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-variable language_\">console\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">error\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;加载仪表盘数据失败:&#x27;\u003C\u002Fspan>, err)\n    } \u003Cspan class=\"hljs-keyword\">finally\u003C\u002Fspan> {\n      loading.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\n    }\n  }\n\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> {\n    userInfo,\n    productList,\n    orderStats,\n    loading,\n    error,\n    loadDashboardData\n  }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch4 data-line=\"155\" id=\"💡 进阶：处理部分失败的情况\">💡 进阶：处理部分失败的情况\u003C\u002Fh4>\n\u003Cp data-line=\"157\">\u003Ccode>Promise.all\u003C\u002Fcode> 的“一损俱损”特性在某些场景下可能不适用。例如，加载一个页面时，关键数据必须成功，但一些可选的推荐数据即使失败也不应影响页面的正常展示。这时，\u003Ccode>Promise.allSettled\u003C\u002Fcode> 就派上用场了。\u003C\u002Fp>\n\u003Cp data-line=\"159\">\u003Ccode>Promise.allSettled\u003C\u002Fcode> 会等待所有传入的 Promise 完成（无论成功或失败），并返回一个数组，其中包含每个 Promise 的结果对象（包含 \u003Ccode>status\u003C\u002Fcode>、\u003Ccode>value\u003C\u002Fcode> 或 \u003Ccode>reason\u003C\u002Fcode>）。\u003C\u002Fp>\n\u003Cp data-line=\"161\">\u003Cstrong>代码示例：\u003C\u002Fstrong>\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"163\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">javascript\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-javascript\" language=javascript>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-keyword\">async\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">robustDataLoad\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> promises = [\n    \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Fcritical-data&#x27;\u003C\u002Fspan>), \u003Cspan class=\"hljs-comment\">\u002F\u002F 关键数据\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Foptional-data-1&#x27;\u003C\u002Fspan>), \u003Cspan class=\"hljs-comment\">\u002F\u002F 可选数据\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-title function_\">fetch\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Foptional-data-2&#x27;\u003C\u002Fspan>)  \u003Cspan class=\"hljs-comment\">\u002F\u002F 可选数据\u003C\u002Fspan>\n  ]\n\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> results = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Promise\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">allSettled\u003C\u002Fspan>(promises)\n\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> criticalData = results[\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>].\u003Cspan class=\"hljs-property\">status\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;fulfilled&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> results[\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>].\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>() : \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> optionalData1 = results[\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>].\u003Cspan class=\"hljs-property\">status\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;fulfilled&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> results[\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>].\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>() : \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> optionalData2 = results[\u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>].\u003Cspan class=\"hljs-property\">status\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;fulfilled&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> results[\u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>].\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">json\u003C\u002Fspan>() : \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>\n\n  \u003Cspan class=\"hljs-comment\">\u002F\u002F 即使可选数据加载失败，关键数据仍然可用\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (criticalData) {\n    \u003Cspan class=\"hljs-title function_\">renderPage\u003C\u002Fspan>(criticalData, optionalData1, optionalData2)\n  } \u003Cspan class=\"hljs-keyword\">else\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-title function_\">showError\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;关键数据加载失败&#x27;\u003C\u002Fspan>)\n  }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Ch4 data-line=\"186\" id=\"📌 总结与最佳实践\">📌 总结与最佳实践\u003C\u002Fh4>\n\u003Ctable data-line=\"188\">\n\u003Cthead data-line=\"188\">\n\u003Ctr data-line=\"188\">\n\u003Cth style=\"text-align:left\">执行方式\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">核心语法\u003C\u002Fth>\n\u003Cth style=\"text-align:left\">适用场景\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody data-line=\"190\">\n\u003Ctr data-line=\"190\">\n\u003Ctd style=\"text-align:left\">\u003Cstrong>串行执行\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">\u003Ccode>async\u002Fawait\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">请求之间有强依赖关系，后一个请求需要前一个的结果。\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr data-line=\"191\">\n\u003Ctd style=\"text-align:left\">\u003Cstrong>并行执行\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">\u003Ccode>Promise.all\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">多个请求相互独立，需要等待所有请求都成功后再统一处理。\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr data-line=\"192\">\n\u003Ctd style=\"text-align:left\">\u003Cstrong>容错并行\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">\u003Ccode>Promise.allSettled\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:left\">多个请求相互独立，即使部分失败也需要获取成功的结果。\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp data-line=\"194\">\u003Cstrong>最佳实践：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Col data-line=\"196\">\n\u003Cli data-line=\"196\">\u003Cstrong>始终使用 \u003Ccode>try...catch\u003C\u002Fcode>\u003C\u002Fstrong>：异步操作随时可能失败，使用 \u003Ccode>try...catch\u003C\u002Fcode> 块可以优雅地捕获和处理错误，避免应用崩溃。\u003C\u002Fli>\n\u003Cli data-line=\"197\">\u003Cstrong>管理加载状态\u003C\u002Fstrong>：为用户提供清晰的反馈，例如在请求开始时显示加载指示器，请求结束后隐藏。\u003C\u002Fli>\n\u003Cli data-line=\"198\">\u003Cstrong>封装可复用的逻辑\u003C\u002Fstrong>：将异步请求逻辑封装在组合式函数（Composables）中，可以提高代码的复用性和可维护性。\u003C\u002Fli>\n\u003Cli data-line=\"199\">\u003Cstrong>请求取消\u003C\u002Fstrong>：在组件卸载时，考虑使用 \u003Ccode>AbortController\u003C\u002Fcode> 取消未完成的请求，防止内存泄漏和状态更新错误。\u003C\u002Fli>\n\u003C\u002Fol>\n",5219,17,"0","",2,0,388,"请求,&#x,Promise,value,执行","在 Vue 3 的开发中，处理异步请求是家常便饭。我们经常遇到这样的场景：需要等待一个或多个 API 请求完成后，才能进行下一步的数据处理或页面渲染。如何清晰、高效地控制这些请求的执行顺序，是构建健壮应用的关键。\n本文将探讨在 Vue 3 中控制异步请求的两种核心模式：串行执行和并行执行，并结合 async\u002Fawait、Promise.","2026-04-03 17:34:26"]