JavaScript的 async , await 笔记250808

JavaScript的 async , await 笔记250808

JavaScript 异步编程:async/await 深度解析

asyncawait 由 ES2017 引入, 是 Promise 的语法糖

async function f() { return 123; } 等价于 function f() { return Promise.resolve(123); }


async function fun() { return "hello" ; }

👆等价于👇 function fun() { return Promise.resolve("hello"); }


在现代 JavaScript 开发中,async/await 是处理异步操作的核心语法,由 ES2017 引入, 是 Promise 的语法糖, 它让异步代码拥有同步代码的可读性和可维护性。

核心概念

async 函数

  • 声明一个异步函数:async function myFunction() {}
  • 总是返回一个 Promise 对象
  • 函数内可以使用 await 关键字
javascript 复制代码
async function fetchData() {
  return "Data"; // 等价于 Promise.resolve("Data")
}

fetchData().then(data => console.log(data)); // 输出 "Data"

await 表达式

  • 只能在 async 函数内部使用
  • 暂停 async 函数执行,等待 Promise 完成
  • 返回 Promise 的解析值
  • 如果等待的不是 Promise,则直接返回该值
javascript 复制代码
async function getUser() {
  const response = await fetch('/api/user');
  const user = await response.json();
  return user;
}

错误处理机制

try/catch 模式 async/await 可以使用同步的错误处理方式:

javascript 复制代码
async function loadData() {
  try {
    const data = await fetchData();
    const processed = await processData(data);
    return processed;
  } catch (error) {
    console.error("加载失败:", error);
    return fallbackData;
  } finally {
    console.log("加载过程结束");
  }
}

错误传播特性 未处理的错误会向外层传播:

javascript 复制代码
async function taskA() {
  throw new Error("A失败");
}

async function taskB() {
  await taskA(); // 这里会抛出错误
}

taskB().catch(err => console.log(err.message)); // 输出 "A失败"

执行流程解析

微观任务队列 async/await 基于 Promise 的微任务机制:

javascript 复制代码
console.log("开始");

async function asyncTask() {
  console.log("async开始");
  await Promise.resolve();
  console.log("async继续");
}

asyncTask();

Promise.resolve().then(() => console.log("Promise微任务"));

console.log("结束");

// 输出顺序:
// 开始
// async开始
// 结束
// Promise微任务
// async继续

并发执行优化 避免不必要的顺序等待:

javascript 复制代码
// 低效 - 顺序执行
async function sequentialFetch() {
  const user = await fetch('/user');
  const posts = await fetch('/posts');
  return { user, posts };
}

// 高效 - 并发执行
async function parallelFetch() {
  const [user, posts] = await Promise.all([
    fetch('/user'),
    fetch('/posts')
  ]);
  return { user, posts };
}

高级用法与模式

1. 异步迭代 处理数据流或分页请求:

javascript 复制代码
async function processItems(items) {
  for await (const item of items) {
    await processItem(item);
  }
}

2. 顶层 await 在模块中使用顶层 await(ES2022):

javascript 复制代码
// module.js
const data = await fetchData();
export default data;

3. 异步错误边界 创建错误处理包装器:

javascript 复制代码
function withErrorHandling(fn) {
  return async (...args) => {
    try {
      return await fn(...args);
    } catch (error) {
      console.error("操作失败:", error);
      throw error;
    }
  };
}

const safeFetch = withErrorHandling(fetch);

4. 取消异步操作 使用 AbortController:

javascript 复制代码
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(timeoutId);
    return response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error("请求超时");
    }
    throw error;
  }
}

最佳实践指南

1. 避免阻塞陷阱

javascript 复制代码
// 错误:顺序执行独立操作
const a = await getA();
const b = await getB(); // 不依赖a,但会等待

// 正确:并发执行
const [a, b] = await Promise.all([getA(), getB()]);

2. 合理使用 try/catch

javascript 复制代码
// 只捕获必要的错误
async function updateUser(user) {
  try {
    await validateUser(user);
  } catch (error) {
    // 只处理验证错误
    return { error: "验证失败" };
  }
  
  // 其他操作不需要捕获
  await saveUser(user);
  return { success: true };
}

3. 处理并行任务

javascript 复制代码
// 限制并发数
async function processBatch(items, concurrency = 5) {
  const results = [];
  
  for (let i = 0; i < items.length; i += concurrency) {
    const batch = items.slice(i, i + concurrency);
    const batchResults = await Promise.all(
      batch.map(item => processItem(item))
    );
    results.push(...batchResults);
  }
  
  return results;
}

4. 组合 Promise 和 async/await

javascript 复制代码
// 在事件处理中立即执行异步操作
button.addEventListener('click', () => {
  (async () => {
    try {
      const data = await fetchData();
      updateUI(data);
    } catch (error) {
      showError(error);
    }
  })();
});

常见误区与解决方案

1. 忘记 await

javascript 复制代码
async function saveData() {
  const data = fetchData(); // 错误:缺少await
  // data是Promise对象,不是实际数据
  
  const data = await fetchData(); // 正确
}

2. 过度顺序化

javascript 复制代码
// 低效写法
async function process() {
  const a = await step1();
  const b = await step2(a);
  const c = await step3(b);
  return c;
}

// 优化后
async function optimizedProcess() {
  const a = step1(); // 立即开始
  const b = step2(await a); // 等待a完成后开始
  return step3(await b);
}

3. 错误处理遗漏

javascript 复制代码
// 危险:未处理错误
async function dangerousTask() {
  await criticalOperation();
}

// 安全:添加错误处理
async function safeTask() {
  try {
    await criticalOperation();
  } catch (error) {
    logError(error);
    throw error; // 或者处理错误
  }
}

async/await 与 Promise 对比

特性 Promise async/await
可读性 链式调用,嵌套复杂 类似同步代码,直观清晰
错误处理 .catch() 方法 try/catch 结构
调试 链式调用调试困难 可直接断点调试
流程控制 .then() 链式控制 使用标准控制结构
返回值 总是返回 Promise 总是返回 Promise

总结

async/await 是现代 JavaScript 异步编程的核心特性,它:

  1. 让异步代码拥有同步代码的可读性和可维护性
  2. 通过 try/catch 提供更自然的错误处理
  3. 兼容 Promise 所有功能,可混合使用
  4. 支持高级模式如并发控制、取消操作
  5. 需要理解其底层基于 Promise 的微任务机制

掌握 async/await 的关键是理解其执行流程和错误处理机制,避免常见陷阱,并合理利用其与 Promise 的组合能力。





1️⃣ 参考 1

在 JavaScript 中,async/await 是处理异步操作的语法糖,基于 Promise 实现,使异步代码更接近同步代码的书写风格。以下是详细解析:


核心概念

  1. async 函数

    • 声明:在函数前添加 async 关键字
    • 返回值:总是返回一个 Promise 对象
      • 函数内返回普通值 → 自动包装为 resolved 状态的 Promise
      • 函数内抛出错误 → 返回 rejected 状态的 Promise
    javascript 复制代码
    async function foo() {
      return 42; // 等价于 Promise.resolve(42)
    }
    foo().then(console.log); // 输出 42
  2. await 表达式

    • 只能在 async 函数内部使用
    • 作用:暂停 async 函数的执行 ,等待 Promise 完成
      • 若 Promise 成功:返回解决值(resolved value)
      • 若 Promise 失败:抛出拒绝原因(rejection reason)
    javascript 复制代码
    async function bar() {
      const result = await new Promise(resolve => 
        setTimeout(() => resolve("Done!"), 1000)
      );
      console.log(result); // 1秒后输出 "Done!"
    }

执行流程详解

javascript 复制代码
async function example() {
  console.log("Start");
  const data = await fetchData(); // 暂停执行,等待 Promise
  console.log("Data received:", data);
  return "Complete";
}

example()
  .then(console.log) // 输出 "Complete"
  .catch(console.error);

console.log("After call"); // 先于异步操作输出

执行顺序:

  1. 输出 "Start"
  2. 遇到 await → 暂停 example 函数,交出控制权
  3. 输出 "After call"
  4. fetchData() 的 Promise 完成:
    • 成功:继续执行,输出 "Data received: ..."
    • 失败:抛出错误,被 .catch() 捕获

错误处理

  1. try/catch 捕获同步错误和异步拒绝

    javascript 复制代码
    async function fetchWithError() {
      try {
        const data = await fetch("invalid-url");
        return data.json();
      } catch (err) {
        console.error("Request failed:", err); // 捕获网络错误或 JSON 解析错误
      }
    }
  2. 链式 .catch()

    javascript 复制代码
    fetchWithError()
      .catch(err => console.error("Fallback:", err));

关键特性

  1. 非阻塞性

    javascript 复制代码
    async function parallel() {
      const [a, b] = await Promise.all([task1(), task2()]); // 并行执行
      await task3(); // 顺序执行
    }
  2. 顶层 await (ES2022)

    • 在模块顶层直接使用 await
    javascript 复制代码
    // 模块中
    const data = await fetchData();
    console.log(data);

常见误区

  1. await 不会阻塞主线程

    • 仅暂停当前 async 函数,不影响其他代码执行
  2. 忘记 await

    javascript 复制代码
    async function mistake() {
      const p = fetchData(); // 缺少 await → 返回 Promise 对象而非结果
      console.log(p); // 输出 Promise { <pending> }
    }
  3. 循环中的 await

    javascript 复制代码
    // 顺序执行 (可能低效)
    for (const url of urls) {
      await fetch(url);
    }
    
    // 并行执行 (推荐)
    await Promise.all(urls.map(url => fetch(url)));

最佳实践

  1. try/catch 替代 .then() 链式调用
  2. 无依赖的异步操作用 Promise.all() 并行处理
  3. 优先使用 async/await 而非回调函数/Promise 链

浏览器支持:所有现代浏览器(Chrome 55+、Firefox 52+、Safari 10.1+)及 Node.js 7.6+





2️⃣ 参考 2

JavaScript asyncawait 详解

asyncawait 是 ES2017 引入的语法糖,用于简化 Promise 的使用,使异步代码看起来更像同步代码,同时保持非阻塞特性。

核心概念

1. async 函数

  • 作用:声明一个异步函数
  • 返回值 :总是返回一个 Promise 对象
    • 函数返回普通值 → 自动包装为 resolved Promise
    • 函数抛出错误 → 自动包装为 rejected Promise
    • 函数返回 Promise → 直接返回该 Promise
javascript 复制代码
async function basicAsync() {
  return 42; // 等同于 Promise.resolve(42)
}

async function rejectedAsync() {
  throw new Error("失败"); // 等同于 Promise.reject(new Error("失败"))
}

basicAsync().then(console.log); // 42
rejectedAsync().catch(console.error); // Error: 失败

2. await 表达式

  • 作用:暂停异步函数执行,等待 Promise 解决
  • 语法const result = await promise
  • 行为
    • 如果 Promise 成功解决 → 返回解决值
    • 如果 Promise 被拒绝 → 抛出拒绝原因(相当于 throw
  • 限制 :只能在 async 函数内部使用
javascript 复制代码
async function fetchUser() {
  const response = await fetch('/api/user'); // 等待 fetch 完成
  const user = await response.json(); // 等待 JSON 解析
  return user;
}

执行流程解析

基本执行顺序

javascript 复制代码
console.log("开始");

async function demo() {
  console.log("函数开始");
  const result = await Promise.resolve("结果");
  console.log("await 完成:", result);
  return "完成";
}

demo().then(res => console.log("then:", res));
console.log("结束");

/* 输出顺序:
   开始
   函数开始
   结束
   await 完成: 结果
   then: 完成
*/

执行机制图解

sequenceDiagram participant M as 主线程 participant A as async 函数 participant P as Promise M->>A: 调用 async 函数 A->>A: 同步代码执行 A->>P: await 暂停函数执行 M->>M: 继续执行后续代码 P-->>A: Promise 解决 A->>A: 恢复执行 A->>M: 返回最终结果

错误处理

1. try/catch 方式(推荐)

javascript 复制代码
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    if (!response.ok) {
      throw new Error('网络响应异常');
    }
    return await response.json();
  } catch (error) {
    console.error('请求失败:', error);
    return { data: null }; // 降级处理
  }
}

2. .catch() 方式

javascript 复制代码
async function loadResource() {
  const resource = await fetchResource().catch(handleError);
  if (!resource) return;
  
  // 继续处理资源
}

function handleError(error) {
  console.error('加载失败:', error);
  return null;
}

性能优化技巧

1. 并行执行(避免顺序等待)

javascript 复制代码
// 顺序执行(慢)
async function sequential() {
  const user = await fetchUser();
  const posts = await fetchPosts(user.id);
  return { user, posts };
}

// 并行执行(快)
async function parallel() {
  const [user, posts] = await Promise.all([
    fetchUser(),
    fetchPosts() // 不需要等待用户数据
  ]);
  return { user, posts };
}

2. 提前启动请求

javascript 复制代码
async function loadPage() {
  // 提前启动不依赖彼此的数据请求
  const userPromise = fetchUser();
  const settingsPromise = fetchSettings();
  
  // 处理其他逻辑...
  renderHeader();
  
  // 等待需要的数据
  const [user, settings] = await Promise.all([
    userPromise, 
    settingsPromise
  ]);
  
  renderContent(user, settings);
}

高级使用模式

1. 异步迭代器(for-await-of)

javascript 复制代码
async function processItems(items) {
  for await (const item of asyncIterator(items)) {
    console.log('处理:', item);
    await processItem(item); // 可以包含异步操作
  }
}

2. 异步构造函数模式

javascript 复制代码
class DataLoader {
  constructor() {
    // 保存初始化Promise
    this.initialized = this.init();
  }
  
  async init() {
    this.config = await loadConfig();
    this.cache = await createCache();
  }
  
  async getData() {
    await this.initialized; // 确保初始化完成
    return fetchData(this.config.endpoint);
  }
}

// 使用
const loader = new DataLoader();
loader.getData().then(console.log);

3. 超时控制

javascript 复制代码
function withTimeout(promise, timeout) {
  return Promise.race([
    promise,
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('操作超时')), timeout)
    )
  ]);
}

async function fetchWithTimeout() {
  try {
    return await withTimeout(fetch('/api'), 5000);
  } catch (error) {
    if (error.message === '操作超时') {
      console.log('请求超时,使用缓存');
      return cachedData;
    }
    throw error;
  }
}

常见误区与解决方案

1. 在循环中错误使用 await

javascript 复制代码
// ❌ 错误:顺序执行异步操作,效率低
async function processArray(array) {
  for (const item of array) {
    await processItem(item); // 每个await都会阻塞循环
  }
}

// ✅ 正确:并行处理
async function processArray(array) {
  // 并行启动所有操作
  const promises = array.map(item => processItem(item));
  // 等待所有操作完成
  await Promise.all(promises);
}

2. 忘记处理错误

javascript 复制代码
// ❌ 错误:未处理的Promise拒绝
async function unsafeFetch() {
  const data = await fetch('/api');
  return data.json();
}

// ✅ 正确:添加错误处理
async function safeFetch() {
  try {
    const response = await fetch('/api');
    return await response.json();
  } catch (error) {
    console.error('请求失败:', error);
    return { status: 'error' };
  }
}

3. 不必要地使用 await

javascript 复制代码
// ❌ 错误:不必要的await
async function getData() {
  const data = await fetchData(); 
  return await processData(data); // 第二个await不必要
}

// ✅ 正确:直接返回Promise
async function getData() {
  const data = await fetchData();
  return processData(data); // 返回Promise
}

async/await 与 Promise 对比

特性 Promise async/await
代码结构 链式调用 同步风格
错误处理 .catch() 方法 try/catch
可读性 回调嵌套可能复杂 线性执行更清晰
调试 断点在then回调间跳转 像同步代码一样调试
循环处理 需要Promise.all等辅助 for-await-of 简化
返回值 显式返回Promise 自动包装为Promise

最佳实践

  1. 优先使用 async/await 代替 Promise 链式调用

  2. 始终处理错误 - 使用 try/catch 或 .catch()

  3. 避免阻塞操作 - 不要在 async 函数中执行长时间同步任务

  4. 并行化独立操作 - 使用 Promise.all 加速执行

  5. 合理使用箭头函数 - 简化小规模异步操作:

    javascript 复制代码
    const fetchData = async () => {
      const res = await fetch('/data');
      return res.json();
    };

浏览器与Node.js支持

环境 支持版本
Chrome 55+
Firefox 52+
Safari 10.1+
Edge 15+
Node.js 7.6+ (8.0+ 完全支持)

对于旧环境,可使用 Babel 等工具转译 async/await 语法。

总结

asyncawait 使异步代码:

  1. 更易读 - 类似同步代码的结构
  2. 更易写 - 减少回调嵌套
  3. 更易调试 - 错误堆栈更清晰
  4. 更易维护 - 线性执行流程

虽然本质上是 Promise 的语法糖,但 async/await 显著提高了异步代码的开发体验和可维护性,是现代 JavaScript 异步编程的首选方式。





3️⃣ 参考 3

在 JavaScript 中,asyncawait 是处理异步操作的语法糖,基于 Promise 实现,旨在让异步代码的书写和阅读更接近同步方式。以下是详细解析:


1. ** async 函数**

  • 作用:声明一个异步函数,始终返回一个 Promise 对象。

  • 行为

    • 函数返回值会被自动包装成 Promise.resolve(value)
    • 抛出错误时返回 Promise.reject(error)
  • 示例

    javascript 复制代码
    async function foo() {
      return 42; // 等价于 Promise.resolve(42)
    }
    foo().then(console.log); // 输出 42

2. ** await 表达式**

  • 作用 :暂停 async 函数的执行,等待 Promise 完成(resolve/reject)。

  • 规则

    • 只能在 async 函数内部使用。
    • 等待 Promise 解决后返回结果值。
    • 若 Promise 拒绝,抛出错误(可用 try/catch 捕获)。
  • 示例

    javascript 复制代码
    async function fetchData() {
      const response = await fetch('https://api.example.com/data'); // 等待请求完成
      const data = await response.json(); // 等待 JSON 解析
      return data;
    }

3. 错误处理

  • 使用 try/catch 捕获 await 的异常:

    javascript 复制代码
    async function loadData() {
      try {
        const data = await fetchData();
      } catch (error) {
        console.error("请求失败:", error);
      }
    }
  • 也可用 .catch() 处理整个 async 函数的错误:

    javascript 复制代码
    loadData().catch(error => console.error(error));

4. 执行机制

  • 非阻塞性await 不会阻塞主线程,仅暂停当前 async 函数的后续代码。

  • 微任务调度:当 Promise 解决时,后续代码作为微任务加入队列。

  • 示例分析

    javascript 复制代码
    console.log("Start");
    
    async function demo() {
      console.log("Async start");
      await Promise.resolve(); // 将后续代码转为微任务
      console.log("Async end");
    }
    
    demo();
    console.log("End");
    
    // 输出顺序:
    // Start → Async start → End → Async end

5. 并行优化

  • 避免串行等待:多个独立操作应并行执行以提高效率。

  • 使用 Promise.all

    javascript 复制代码
    async function fetchAll() {
      const [user, posts] = await Promise.all([
        fetch('/user'),
        fetch('/posts')
      ]);
      // 两个请求并行执行
    }

6. 常见误区

  • 在顶层使用 await
    ES2022 支持在模块顶层使用 await(需在模块中):

    javascript 复制代码
    // 模块文件内
    const data = await fetchData(); // 合法
  • 循环中的 await
    在循环内使用 await 会导致串行执行,需用 Promise.all 优化:

    javascript 复制代码
    // 串行(慢):
    for (const url of urls) {
      await fetch(url);
    }
    
    // 并行(快):
    await Promise.all(urls.map(url => fetch(url)));

7. 与传统 Promise 对比

场景 Promise 写法 async/await 写法
链式调用 .then().then() 顺序书写,无嵌套
错误处理 .catch() try/catch 同步风格
代码可读性 嵌套复杂时难读 结构清晰,类似同步代码

总结

  • async:标记异步函数,隐式返回 Promise。
  • await:暂停函数执行,等待 Promise 结果。
  • 优势:以同步形式写异步代码,提升可读性和可维护性。
  • 最佳实践 :结合 try/catch 处理错误,用 Promise.all 优化并行任务。

通过合理使用 async/await,可显著简化异步逻辑,减少回调地狱,写出更优雅的 JavaScript 代码。





4️⃣ 参考 4

JavaScript 的 async 和 await 详解

async 和 await 是 ES2017 (ES8) 引入的 JavaScript 特性,它们建立在 Promise 之上,提供了更简洁、更直观的方式来处理异步操作。这两个关键字彻底改变了我们编写异步代码的方式,使异步代码看起来更像同步代码。

核心概念

1. async 函数

  • 用于声明一个异步函数
  • 始终返回一个 Promise 对象
  • 函数内部可以使用 await 关键字
javascript 复制代码
async function fetchData() {
  return "Data fetched"; // 自动包装为 resolved Promise
}

// 等同于
function fetchData() {
  return Promise.resolve("Data fetched");
}

2. await 表达式

  • 只能在 async 函数内部使用
  • 暂停 async 函数的执行,等待 Promise 解决
  • 返回 Promise 的解决值
  • 如果 Promise 被拒绝,则抛出错误
javascript 复制代码
async function getUser() {
  const response = await fetch('/api/user'); // 等待 fetch 完成
  const user = await response.json(); // 等待 JSON 解析
  return user;
}

工作原理详解

1. async 函数的行为

javascript 复制代码
async function example() {
  console.log("Start");
  const result = await new Promise(resolve => 
    setTimeout(() => resolve("Done!"), 1000)
  );
  console.log(result);
  return "Finished";
}

const promise = example();
console.log("After calling example");

// 输出顺序:
// Start
// After calling example
// (1秒后) Done!

2. await 的底层机制 await 本质上是一个语法糖,它将代码分割为多个微任务:

javascript 复制代码
// 使用 await 的代码
async function example() {
  const a = await task1();
  const b = await task2(a);
  return b;
}

// 底层等效代码
function example() {
  return task1().then(a => {
    return task2(a).then(b => {
      return b;
    });
  });
}

错误处理

1. try/catch 方式

javascript 复制代码
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    // 返回默认值或重新抛出错误
    return { defaultValue: true };
  }
}

2. 结合 catch 方法

javascript 复制代码
async function loadContent() {
  const data = await fetchData().catch(handleError);
  if (!data) return; // 处理错误情况
  
  // 使用数据
  renderContent(data);
}

function handleError(error) {
  console.error('Loading failed:', error);
  showErrorMessage();
  return null;
}

高级用法与模式

1. 并行执行多个异步操作

javascript 复制代码
async function fetchAllData() {
  // 同时启动所有请求
  const [user, posts, comments] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchComments()
  ]);
  
  return { user, posts, comments };
}

2. 循环中的 await

javascript 复制代码
// 顺序执行
async function processItems(items) {
  for (const item of items) {
    await processItem(item); // 等待每个完成
  }
}

// 并行执行
async function processItemsFast(items) {
  await Promise.all(items.map(item => processItem(item)));
}

3. 立即执行 async 函数

javascript 复制代码
(async () => {
  try {
    const result = await someAsyncOperation();
    console.log(result);
  } catch (error) {
    console.error(error);
  }
})();

4. 类中的 async 方法

javascript 复制代码
class ApiClient {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }
  
  async get(endpoint) {
    const response = await fetch(`${this.baseUrl}/${endpoint}`);
    return response.json();
  }
  
  async post(endpoint, data) {
    const response = await fetch(`${this.baseUrl}/${endpoint}`, {
      method: 'POST',
      body: JSON.stringify(data)
    });
    return response.json();
  }
}

// 使用示例
const client = new ApiClient('https://api.example.com');
const userData = await client.get('user/123');

性能优化技巧

1. 避免不必要的顺序等待

javascript 复制代码
// 低效 - 顺序执行
async function inefficient() {
  const a = await fetchA();
  const b = await fetchB(); // 等待 fetchA 完成才开始
  
  return { a, b };
}

// 高效 - 并行执行
async function efficient() {
  const [a, b] = await Promise.all([fetchA(), fetchB()]);
  return { a, b };
}

2. 使用 Promise 缓存

javascript 复制代码
const userCache = {};

async function getUser(id) {
  if (!userCache[id]) {
    userCache[id] = fetch(`/api/users/${id}`).then(r => r.json());
  }
  return userCache[id];
}

3. 控制并发数量

javascript 复制代码
async function runWithConcurrency(tasks, maxConcurrent) {
  const results = [];
  const executing = [];
  
  for (const task of tasks) {
    const p = task().then(r => {
      executing.splice(executing.indexOf(p), 1);
      return r;
    });
    
    results.push(p);
    executing.push(p);
    
    if (executing.length >= maxConcurrent) {
      await Promise.race(executing);
    }
  }
  
  return Promise.all(results);
}

常见陷阱与解决方案

1. 忘记 await

javascript 复制代码
async function updateData() {
  // 错误:忘记 await
  saveToDatabase(data); // 返回的 Promise 未被处理
  
  // 正确
  await saveToDatabase(data);
}

2. 循环中意外顺序执行

javascript 复制代码
const urls = ['/data1', '/data2', '/data3'];

// 错误:顺序执行(可能不是预期行为)
urls.forEach(async url => {
  const data = await fetch(url);
  console.log(data);
});

// 正确:并行执行
await Promise.all(urls.map(async url => {
  const data = await fetch(url);
  console.log(data);
}));

3. 忽略错误处理

javascript 复制代码
// 危险:未处理的拒绝
async function dangerous() {
  const data = await fetchData();
  process(data);
}

// 安全:添加错误处理
async function safe() {
  try {
    const data = await fetchData();
    process(data);
  } catch (error) {
    console.error('Operation failed', error);
  }
}

async/await 与 Promise 的对比

1. 可读性比较

javascript 复制代码
// Promise 链
function getUserPosts(userId) {
  return fetchUser(userId)
    .then(user => fetchPosts(user.id))
    .then(posts => processPosts(posts))
    .catch(error => console.error(error));
}

// async/await
async function getUserPosts(userId) {
  try {
    const user = await fetchUser(userId);
    const posts = await fetchPosts(user.id);
    return processPosts(posts);
  } catch (error) {
    console.error(error);
  }
}

2. 错误处理比较

javascript 复制代码
// Promise 错误处理
fetchData()
  .then(process)
  .catch(handleError);

// async/await 错误处理
try {
  const data = await fetchData();
  process(data);
} catch (error) {
  handleError(error);
}

浏览器与 Node.js 支持

1. 浏览器支持

  • Chrome 55+ (2016)
  • Firefox 52+ (2017)
  • Safari 10.1+ (2017)
  • Edge 15+ (2017)
  • Internet Explorer: 不支持

2. Node.js 支持

  • Node.js 7.6+ (需要启用 --harmony 标志)
  • Node.js 8.0+ 完全支持

3. 向后兼容方案 使用 Babel 转译 async/await:

json 复制代码
// .babelrc
{
  "presets": ["@babel/preset-env"]
}

最佳实践

  1. 始终处理错误:使用 try/catch 或 .catch()

  2. 避免过度顺序化:并行执行独立任务

  3. 合理命名 async 函数:使用动词前缀如 fetch、load、get

  4. 文档化异步函数 :使用 JSDoc 注释

    javascript 复制代码
    /**
     * 获取用户数据
     * @async
     * @param {string} userId - 用户ID
     * @returns {Promise<Object>} 用户数据对象
     */
    async function getUser(userId) {
      // ...
    }
  5. 结合传统 Promise:在需要时混合使用

  6. 优化递归函数 :使用尾调用优化

    javascript 复制代码
    async function processQueue(queue) {
      while (queue.length > 0) {
        const item = queue.shift();
        await processItem(item);
      }
    }

实际应用案例

1. API 请求处理

javascript 复制代码
async function fetchPaginatedData(url) {
  let allData = [];
  let nextUrl = url;
  
  while (nextUrl) {
    const response = await fetch(nextUrl);
    if (!response.ok) throw new Error('API request failed');
    
    const data = await response.json();
    allData = allData.concat(data.results);
    nextUrl = data.next; // 假设 API 返回 next 字段
  }
  
  return allData;
}

2. 超时控制

javascript 复制代码
function withTimeout(promise, timeout) {
  return new Promise(async (resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Operation timed out'));
    }, timeout);
    
    try {
      const result = await promise;
      clearTimeout(timer);
      resolve(result);
    } catch (error) {
      clearTimeout(timer);
      reject(error);
    }
  });
}

// 使用
try {
  const data = await withTimeout(fetchData(), 5000);
} catch (error) {
  if (error.message === 'Operation timed out') {
    // 处理超时
  } else {
    // 处理其他错误
  }
}

3. 逐步加载 UI

javascript 复制代码
async function loadDashboard() {
  showLoadingSpinner();
  
  try {
    // 并行加载独立部分
    const [user, notifications, timeline] = await Promise.all([
      loadUserData(),
      loadNotifications(),
      loadTimeline()
    ]);
    
    // 顺序加载依赖部分
    const recommendations = await loadRecommendations(user.interests);
    
    renderDashboard({ user, notifications, timeline, recommendations });
  } catch (error) {
    showError(error);
  } finally {
    hideLoadingSpinner();
  }
}

总结

async/await 是现代 JavaScript 异步编程的核心特性:

  1. async 函数

    • 声明异步函数
    • 总是返回 Promise
    • 内部可以使用 await
  2. await 表达式

    • 暂停执行直到 Promise 解决
    • 返回 Promise 的解决值
    • 抛出拒绝原因
  3. 优势

    • 代码更简洁、可读
    • 错误处理更直观(try/catch)
    • 调试更简单(调用栈更清晰)
    • 逻辑更接近同步代码
  4. 最佳实践

    • 合理组合并行与顺序执行
    • 始终处理潜在错误
    • 避免不必要的等待
    • 适当使用 Promise 高级特性
  5. 注意事项

    • 只能在 async 函数中使用 await
    • 浏览器兼容性考虑
    • 避免阻塞事件循环

async/await 并没有取代 Promise,而是提供了一种更优雅的使用 Promise 的方式。理解其底层机制和适用场景,可以显著提高异步代码的质量和可维护性。





5️⃣ 参考 5

JavaScript 的 async 和 await 详解

asyncawait 是 ES2017(ES8)引入的语法糖,用于简化 Promise 的异步操作,让异步代码拥有同步代码的书写风格,同时保持非阻塞特性。


核心概念

  1. async 函数

    • 声明一个函数为异步函数:async function foo() {}
    • 返回值会被自动包装成 Promise.resolve(value)
    • 抛出错误时返回 Promise.reject(error)
    javascript 复制代码
    async function getData() {
      return "Hello"; // 等价于 Promise.resolve("Hello")
    }
    getData().then(console.log); // 输出 "Hello"
  2. await 表达式

    • 只能在 async 函数内部使用
    • 暂停当前 async 函数的执行,等待 Promise 状态变为 resolved
    • 返回 Promise 成功的结果值
    • 若 Promise 被 rejected,抛出错误(可用 try/catch 捕获)
    javascript 复制代码
    async function fetchData() {
      const response = await fetch('https://api.example.com/data'); // 等待请求完成
      const data = await response.json(); // 等待解析 JSON
      return data;
    }

执行流程解析

javascript 复制代码
async function example() {
  console.log("Start");
  const result = await new Promise((resolve) => 
    setTimeout(() => resolve("Done!"), 1000)
  );
  console.log(result);
  console.log("End");
}

example();
console.log("After calling example");

输出顺序

sql 复制代码
Start
After calling example
(等待 1 秒)
Done!
End
  • 调用 example() 后,函数同步执行到 await 时暂停。
  • 主线程继续执行外部代码(输出 "After...")。
  • 当 Promise 完成(1 秒后),恢复 example 的执行。

错误处理 使用 try/catch 捕获异步错误:

javascript 复制代码
async function loadData() {
  try {
    const data = await fetch("invalid-url");
    return data.json();
  } catch (error) {
    console.error("请求失败:", error);
  }
}

并行优化 多个独立异步操作时,避免顺序等待:

javascript 复制代码
// 顺序执行(效率低)
async function sequential() {
  const a = await task1(); // 等待完成
  const b = await task2(); // 再等待
  return [a, b];
}

// 并行执行(推荐)
async function parallel() {
  const [a, b] = await Promise.all([task1(), task2()]);
  return [a, b];
}

常见问题与解决方案

  1. 循环中的 await

    在循环中直接使用 await 会导致顺序执行:

    javascript 复制代码
    // 依次等待每个请求完成(慢)
    for (const url of urls) {
      const data = await fetch(url);
    }

    优化方案:并行发起请求

    javascript 复制代码
    const promises = urls.map(url => fetch(url));
    const results = await Promise.all(promises);
  2. 顶层 await

    在 ES2022 中,模块顶层可直接使用 await

    javascript 复制代码
    // 模块中允许
    const data = await fetchData();
    export default data;
  3. await 非 Promise 值
    await 会将非 Promise 值包装为 Promise.resolve(value),等同于直接使用该值。


关键特性总结

特性 说明
语法简洁 消除回调地狱,代码更线性
错误友好 try/catch 统一处理同步/异步错误
基于 Promise 兼容现有 Promise 生态
非阻塞 底层仍是异步操作,不阻塞主线程
调试方便 异步代码栈追踪更清晰

使用场景

  1. 网络请求(fetch/axios
  2. 文件读写(Node.js 的 fs.promises
  3. 数据库操作
  4. 任何需要顺序执行异步操作的场景

注意await 会阻塞当前 async 函数内的后续代码,但不会阻塞主线程其他操作。





6️⃣ 参考 6

以下是关于 JavaScript 中 async/await 的详细解析,采用分步说明和代码示例:


一、基本概念

  1. async 函数

    • 声明方式:async function foo() {}const foo = async () => {}
    • 返回值:始终返回一个 Promise 对象
    • 返回值处理
      • 若返回非 Promise 值,会自动包装为 Promise.resolve(value)
      • 若返回 Promise,则直接传递该 Promise
  2. await 操作符

    • 只能在 async 函数内部使用
    • 作用:暂停代码执行,等待 Promise 完成
    • 返回值:Promise 的 resolved 值
    • 错误处理 :若 Promise 被 reject,会抛出异常(需用 try/catch 捕获)

二、基础用法示例 示例 1:基本流程

javascript 复制代码
async function fetchData() {
  const response = await fetch('https://api.example.com/data'); // 等待请求完成
  const data = await response.json(); // 等待 JSON 解析
  return data; // 等价于 Promise.resolve(data)
}

fetchData()
  .then(data => console.log(data))
  .catch(error => console.error(error));

示例 2:错误处理

javascript 复制代码
async function getUser(id) {
  try {
    const response = await fetch(`/users/${id}`);
    if (!response.ok) throw new Error('User not found');
    return await response.json();
  } catch (error) {
    console.error('Fetch failed:', error);
    return { defaultUser: 'Guest' }; // 返回默认值
  }
}

三、高级技巧 1. 并行执行优化

javascript 复制代码
// ❌ 低效的顺序执行(总耗时 ≈ 1s + 1s = 2s)
async function sequential() {
  const a = await task1(); // 1s
  const b = await task2(); // 1s
  return a + b;
}

// ✅ 高效的并行执行(总耗时 ≈ 1s)
async function parallel() {
  const [a, b] = await Promise.all([task1(), task2()]);
  return a + b;
}

2. 混合 Promise 链

javascript 复制代码
async function hybridFlow() {
  const user = await fetchUser();
  return fetchPosts(user.id)
    .then(posts => posts.filter(p => p.published))
    .catch(() => []);
}

四、关键注意事项

  1. 错误冒泡

    • 未捕获的 await 异常会导致返回的 Promise 被 reject
    • 建议始终使用 try/catch 或在调用链末尾添加 .catch()
  2. 顶层 await(ES2022+):

    javascript 复制代码
    // 仅在 ES Modules 中支持
    const response = await fetch('/data');
  3. 调试优势

    • 异步代码在调用栈中保留完整上下文
    • 可直接在 await 行设置断点

五、与 Promise 的对比

特性 Promise 链 async/await
可读性 横向发展(链式调用) 纵向发展(同步式写法)
错误处理 .catch() 集中处理 try/catch 块处理
中间值操作 需通过 .then() 传递 可直接使用变量
调试体验 需跟踪异步边界 同步代码调试方式

六、常见误区

  1. 忘记 await

    javascript 复制代码
    async function broken() {
      const data = fetchData(); // 错误:未等待,data 是 Promise
      console.log(data); // 输出 Promise 对象
    }
  2. 过度嵌套

    javascript 复制代码
    // 反模式:本可并行执行的任务被序列化
    async function nested() {
      const a = await taskA();
      const b = await taskB(a);
      const c = await taskC(b);
      return c;
    }

七、最佳实践

  1. 保持函数纯净

    javascript 复制代码
    // 推荐:分离业务逻辑与异步控制
    async function process() {
      try {
        const data = await fetchData();
        return validate(data);
      } catch (error) {
        logError(error);
        throw new CustomError('Processing failed');
      }
    }
  2. 合理使用并行

    javascript 复制代码
    async function optimized() {
      const [user, settings] = await Promise.all([
        fetchUser(),
        fetchSettings()
      ]);
      return mergeData(user, settings);
    }
  3. 类型安全(TypeScript):

    typescript 复制代码
    async function typedDemo(): Promise<number> {
      const result: string = await fetchText(); // TS 会提示类型不匹配
      return result.length;
    }

通过掌握这些核心概念和技巧,您可以编写出更清晰、更易维护的异步 JavaScript 代码。建议结合具体业务场景,灵活运用 async/await 的特性。





7️⃣ 参考 7

JavaScript 中 asyncawait 的详解


一、核心概念 asyncawait 是 JavaScript 中用于处理异步操作的现代语法,它们基于 Promise,并提供了更直观、更接近同步代码的写法。以下是它们的核心特性:

  1. async 函数

    • 使用 async 关键字声明的函数会自动返回一个 Promise

    • 即使函数内部没有显式返回 Promise,JavaScript 会将其结果包装成一个 Promise

    • 示例:

      javascript 复制代码
      async function getUser() {
        return { id: 1, name: "张三" }; // 返回值会被自动包装成 Promise
      }
      getUser().then(user => console.log(user)); // 输出: { id: 1, name: "张三" }
  2. await 关键字

    • 只能在 async 函数内部使用。

    • 用于等待一个 Promise 的完成,暂停当前 async 函数的执行,直到 Promise 解决(resolve)或拒绝(reject)。

    • 如果 Promise 被拒绝,await 会抛出异常,可以通过 try...catch 捕获。

    • 示例:

      javascript 复制代码
      async function fetchData() {
        const response = await fetch("https://api.example.com/data"); // 等待网络请求完成
        const data = await response.json(); // 等待数据解析
        return data;
      }

二、优势与传统异步方案对比

传统方案 问题 async/await 的解决方案
回调函数(Callback) 回调地狱(Callback Hell),嵌套过深,代码可读性差。 使用 async/await 将嵌套逻辑扁平化,代码结构更清晰。
Promise 链式调用 链式调用冗长,错误处理分散,中间值传递复杂。 async/await 结合 try...catch 实现集中错误处理,逻辑更直观。
事件监听(Event) 依赖事件触发,难以管理多个异步操作的依赖关系。 async/await 可通过 Promise.all 并行执行多个异步操作,或按需串行执行。

三、核心用法详解

1. async 函数的定义与调用

  • 定义

    javascript 复制代码
    async function myAsyncFunction() {
      // 异步逻辑
    }
  • 调用

    • 返回值是一个 Promise,可以通过 .then()await 获取结果。
    javascript 复制代码
    myAsyncFunction().then(result => {
      console.log(result);
    });

2. await 的使用规则

  • 只能在 async 函数中使用

    javascript 复制代码
    async function example() {
      const result = await someAsyncFunction(); // 合法
    }
    
    // 错误示例:顶层作用域中不能使用 await
    const result = await someAsyncFunction(); // 报错
  • 等待非 Promise

    • 如果 await 后的表达式不是 Promise,会自动将其转换为已解决的 Promise
    javascript 复制代码
    async function test() {
      const value = await 10; // 等价于 await Promise.resolve(10)
      console.log(value); // 输出: 10
    }

3. 错误处理

  • 使用 try...catch 捕获异常

    javascript 复制代码
    async function fetchData() {
      try {
        const response = await fetch("https://api.example.com/data");
        if (!response.ok) throw new Error("请求失败");
        const data = await response.json();
        return data;
      } catch (error) {
        console.error("错误:", error.message);
        throw error; // 可选:向上抛出错误
      }
    }

4. 并行执行多个异步操作

  • 使用 Promise.all 优化性能

    javascript 复制代码
    async function fetchMultipleData() {
      const [data1, data2] = await Promise.all([
        fetch("https://api.example.com/data1").then(res => res.json()),
        fetch("https://api.example.com/data2").then(res => res.json())
      ]);
      console.log(data1, data2);
    }

四、底层原理

1. async 函数的本质

  • 返回 Promise

    • 无论函数内部是否返回 Promiseasync 函数的返回值始终是一个 Promise
    javascript 复制代码
    async function example() {
      return 42; // 等价于 return Promise.resolve(42)
    }
  • 异常处理

    • async 函数中抛出的异常会直接导致返回的 Promise 进入 rejected 状态。
    javascript 复制代码
    async function errorDemo() {
      throw new Error("操作失败"); // 等价于 return Promise.reject("操作失败")
    }

2. await 的作用机制

  • 暂停函数执行,不阻塞主线程

    • await 会暂停当前 async 函数的执行,但不会阻塞其他代码的运行。
    javascript 复制代码
    async function test() {
      console.log(1);
      await new Promise(resolve => setTimeout(resolve, 2000));
      console.log(2);
    }
    console.log(3);
    
    test(); // 输出顺序: 1 -> 3 -> 2

五、常见误区与最佳实践

1. 避免阻塞陷阱

  • 错误示例

    javascript 复制代码
    async function sequential() {
      const a = await task1(); // 等待 1s
      const b = await task2(); // 再等待 1s → 总耗时 2s
    }
  • 正确并行写法

    javascript 复制代码
    async function parallel() {
      const [a, b] = await Promise.all([task1(), task2()]); // 总耗时 1s
    }

**2. 循环中的 await **

  • 错误示例

    javascript 复制代码
    for (const url of urls) {
      await fetch(url); // 每个请求串行执行
    }
  • 正确并行写法

    javascript 复制代码
    const promises = urls.map(url => fetch(url));
    await Promise.all(promises); // 所有请求并行执行

**3. 顶层 await **

  • ES2022+ 模块支持

    javascript 复制代码
    // 模块顶层直接使用 await
    const data = await fetchData();
    export default data;

六、实际应用场景

1. 网络请求

javascript 复制代码
async function getUserPosts(userId) {
  const user = await fetch(`/users/${userId}`);
  const posts = await fetch(`/posts?userId=${userId}`);
  return { user, posts };
}

2. 文件操作(Node.js)

javascript 复制代码
const fs = require("fs").promises;

async function readFiles() {
  const file1 = await fs.readFile("file1.txt", "utf8");
  const file2 = await fs.readFile("file2.txt", "utf8");
  return file1 + file2;
}

3. 定时器控制

javascript 复制代码
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function countdown() {
  console.log(3);
  await delay(1000);
  console.log(2);
  await delay(1000);
  console.log(1);
}

七、总结

1. async/await 的优势

  • 代码更易读:接近同步代码的写法,逻辑清晰。
  • 错误处理更友好 :使用 try...catch 集中处理错误。
  • 调试更方便:调试器可以逐步跟踪每一行代码。
  • 性能优化 :结合 Promise.all 并行执行异步操作。

2. 与传统方案的对比

方案 可读性 错误处理 性能 调试难度
回调函数 分散
Promise 链式调用 分散
async/await 集中

3. 使用建议

  • 优先使用 async/await 替代 Promise 链式调用。
  • 复杂异步逻辑 使用 Promise.allPromise.race 优化。
  • 注意 await 的串行化问题,合理选择并行或串行执行。

通过 async/await,JavaScript 的异步编程变得更加直观和高效。掌握其核心原理和最佳实践,能够显著提升代码的可维护性和开发效率。





8️⃣ 参考 8

async/await 是 ES2017(ES8)引入的异步编程语法糖,基于 Promise 实现,让异步代码的写法更接近同步代码,极大提升了可读性和可维护性。下面详细核心概念**- async:用于声明一个异步函数,该函数的返回值会自动包装为 Promise 对象。

  • await:只能在 async 函数内部使用,用于等待一个 Promise 完成,并获取其结果。

一、 async 函数1. 基本语法通过在函数声明前添加 async 关键字定义异步函数:

javascript 复制代码
// 函数声明
async function fn() {}

// 函数表达式
const fn = async function() {};

// 箭头函数
const fn = async () => {};

2. 返回值特性 async 函数的返回值会被自动包装为 Promise 对象:

  • 若返回非 Promise 值(如数字、对象),等价于 return Promise.resolve(值)
  • 若返回 Promise 对象,则直接返回该 Promise
  • 若抛出错误(throw),等价于 return Promise.reject(错误)
javascript 复制代码
async function demo() {
  return "hello"; // 等价于 return Promise.resolve("hello")
}

demo().then(result => console.log(result)); // 输出:hello

二、 await 关键字1. 基本用法** await 只能在 async 函数内部使用,作用是等待 Promise 完成:**

  • 暂停当前 async 函数的执行,直到等待的 Promise 状态变为 fulfilledrejected
  • 若 Promise 成功(fulfilled),await 返回其结果(resolve 的值)
  • 若 Promise 失败(rejected),await 会抛出异常(需用 try/catch 捕获)
javascript 复制代码
// 模拟一个异步操作
function delay(ms, value) {
  return new Promise(resolve => {
    setTimeout(() => resolve(value), ms);
  });
}

async function asyncFunc() {
  // 等待Promise完成,并获取结果
  const result = await delay(1000, "异步结果");
  console.log(result); // 1秒后输出:异步结果
}

asyncFunc();

2. 错误处理 await 等待的 Promise 若被 reject,会抛出异常,需通过 try/catch 捕获:

javascript 复制代码
function riskyOperation() {
  return new Promise((_, reject) => {
    setTimeout(() => reject(new Error("操作失败")), 1000);
  });
}

async function handleRisk() {
  try {
    const result = await riskyOperation();
    console.log("成功:", result);
  } catch (error) {
    console.log("捕获错误:", error.message); // 输出:操作失败
  }
}

handleRisk();

三、 async/await 的优势1. 解决回调地狱相比 Promise 的 then 链式调用, async/await 让代码更扁平,逻辑更清晰:

javascript 复制代码
// 场景:连续执行3个依赖的异步操作
function step1() { return Promise.resolve(1); }
function step2(num) { return Promise.resolve(num * 2); }
function step3(num) { return Promise.resolve(num + 3); }

// 使用then链式调用
step1()
  .then(r1 => step2(r1))
  .then(r2 => step3(r2))
  .then(r3 => console.log(r3)); // 输出:5

// 使用async/await(更像同步代码)
async function run() {
  const r1 = await step1();
  const r2 = await step2(r1);
  const r3 = await step3(r2);
  console.log(r3); // 输出:5
}
run();

2. 简化条件判断 在异步流程中需要条件判断时,async/awaitthen 链式调用更直观:

javascript 复制代码
async function checkData() {
  const data = await fetchData();
  
  // 基于异步结果的条件判断(自然直观)
  if (data.isValid) {
    const processed = await processData(data);
    console.log(processed);
  } else {
    console.log("数据无效");
  }
}

3. 方便调试 async/await 支持传统的断点调试(then 链式调用的回调函数在调试时跳转频繁)。

四、高级用法1. 并行执行异步操作多个无依赖的异步操作,不要用串行的 await (会浪费时间),应使用 Promise.all() 并行执行:

javascript 复制代码
// 低效:串行执行(总时间 = 1s + 2s = 3s)
async function serial() {
  const r1 = await delay(1000, "结果1");
  const r2 = await delay(2000, "结果2");
  console.log(r1, r2);
}

// 高效:并行执行(总时间 = 2s,取最长的那个)
async function parallel() {
  const promise1 = delay(1000, "结果1");
  const promise2 = delay(2000, "结果2");
  
  // 同时等待两个Promise完成
  const [r1, r2] = await Promise.all([promise1, promise2]);
  console.log(r1, r2);
}

2. 处理多个异步操作的错误 使用 Promise.allSettled() 配合 await,可以获取所有异步操作的结果(无论成功失败):

javascript 复制代码
async function handleMultiple() {
  const promises = [
    Promise.resolve("成功1"),
    Promise.reject(new Error("失败2")),
    Promise.resolve("成功3")
  ];
  
  // 等待所有操作完成(无论成功失败)
  const results = await Promise.allSettled(promises);
  
  // 筛选成功和失败的结果
  const successes = results
    .filter(r => r.status === "fulfilled")
    .map(r => r.value);
    
  const errors = results
    .filter(r => r.status === "rejected")
    .map(r => r.reason);
    
  console.log("成功结果:", successes);
  console.log("错误结果:", errors);
}

五、注意事项

  1. await 只能在 async 函数内部使用,否则会报错。
  2. async 函数不会阻塞主线程,await 只会暂停当前 async 函数的执行。
  3. 未用 try/catch 捕获的 await 错误,会导致整个 async 函数返回的 Promise 被 reject
  4. 避免在循环中使用 await(会导致串行执行),应收集所有 Promise 后用 Promise.all() 处理。

总结

  • async/await 是 Promise 的语法糖,让异步代码更易读
  • async 函数返回 Promise,await 等待 Promise 并获取结果
  • 错误处理通过 try/catch 实现,比 then/catch 更符合同步代码习惯
  • 配合 Promise.all() 可实现高效的并行异步操作

async/await 已成为现代 JavaScript 异步编程的首选方式,掌握它能极大提升代码质量和开发效率。





9️⃣ 参考 9

下面用「从 0 到 1」的思路,把 JavaScript 的 async / await 彻底讲透。

读完你能手写转换、能避坑、能面试。

────────────────

  1. 一句话定位 ──────────────── async / awaitPromise 的语法糖 ,让异步代码看起来像同步代码,但底层仍是 Promise 的微任务队列

──────────────── 2. 基本语法 ────────────────

js 复制代码
async function foo() {
  return 123;          // 相当于 Promise.resolve(123)
}

// 等价于
function foo() {
  return Promise.resolve(123);
}
js 复制代码
async function bar() {
  const val = await somePromise; // 等待 Promise 落定
  return val * 2;
}

──────────────── 3. await 的 4 条硬规则 ────────────────

  1. 只能出现在 async 函数内部(ES2022 顶层 await 除外)。
  2. 右侧表达式如果不是 Promise,会被 Promise.resolve 包裹。
  3. 遇到 await 时,当前函数立即暂停,把后续代码注册成微任务。
  4. 若右侧 Promise reject,会抛出异常(需 try/catch 或 .catch)。

──────────────── 4. 执行顺序(微任务视角) ────────────────

js 复制代码
console.log(1);
async function f() {
  console.log(2);
  await Promise.resolve();
  console.log(3);   // 微任务阶段才执行
}
f();
console.log(4);
// 输出:1 2 4 3

──────────────── 5. 错误处理 ────────────────

js 复制代码
async function load() {
  try {
    const u = await fetch('/user');
    const j = await u.json();
    return j;
  } catch (e) {
    console.error('网络或 JSON 错误', e);
  }
}

try/catch 能捕获:

  • await 后的 Promise reject
  • await 前抛出的同步异常
  • 任何 throw

──────────────── 6. 与 Promise 链等价转换 ────────────────

js 复制代码
// Promise 链
fetch('/user')
  .then(r => r.json())
  .then(j => console.log(j))
  .catch(console.error);

// async/await
async function show() {
  try {
    const r = await fetch('/user');
    const j = await r.json();
    console.log(j);
  } catch (e) {
    console.error(e);
  }
}

──────────────── 7. 并发 vs 串行 ──────────────── 串行(慢):

js 复制代码
const a = await delay(1000);
const b = await delay(1000);

并发(快):

js 复制代码
const [a, b] = await Promise.all([delay(1000), delay(1000)]);

──────────────── 8. async 函数总是返回 Promise ────────────────

js 复制代码
async function f() { return 1; }
f().then(console.log); // 1

即使写了 return 非 Promise,也会被 Promise.resolve() 包裹。

──────────────── 9. 常见坑 & 面试题 ────────────────

  1. 忘记 await
    const data = fetch(url) 拿到的是 Promise 不是结果。

  2. 在循环里串行 await

    js 复制代码
    for (const id of ids) await fetch(id); // 串行
    await Promise.all(ids.map(fetch));     // 并行
  3. 顶层 await

    ES2022 起模块顶层可直接写 await,但仍受微任务调度影响。

  4. 手写 Promise → async 转换

    面试常考:把任何 .then 链改写成 async/await 并保证错误等价。

──────────────── 10. 速查小抄 ────────────────

代码片段 结果/行为
async () => 1 返回 Promise<1>
await 123 立即得到 123(被 Promise.resolve)
await Promise.reject('err') 抛异常,需 try/catch
async () => { throw 'err' } 返回 Promise<rejected>

──────────────── 一句话总结 ──────────────── async 把函数变成"自动 Promise 生成器 ",
await 把 Promise 的 resolve/reject 变成 同步语法

其余所有规则(微任务、错误、并发)与 Promise 完全一致。





🔟 参考 10

JavaScript async/await 详解

一、基本概念 async/await 是 ES2017 引入的异步编程解决方案,基于 Promise 的语法糖,使异步代码具有同步代码的可读性。

核心特点

  1. async 声明异步函数,始终返回 Promise 对象

    javascript 复制代码
    async function fetchData() {
      return "data"; // 等价于 Promise.resolve("data")
    }
  2. await 暂停异步函数执行,等待 Promise 完成

    javascript 复制代码
    async function getUser() {
      const response = await fetch('/api/user'); // 等待请求完成
      return response.json();
    }

二、工作原理

  1. 执行流程

    • 遇到 await 时暂停函数执行,将控制权交还事件循环
    • 等待 Promise 完成后恢复函数执行
    • 不会阻塞主线程(非 CPU 阻塞)
  2. 错误处理

    javascript 复制代码
    async function fetchWithRetry() {
      try {
        const data = await fetchData();
      } catch (error) {
        console.error("请求失败:", error);
      }
    }
  3. 返回值转换

    返回值类型 转换结果
    非 Promise 值 Promise.resolve(value)
    Promise 对象 直接返回该 Promise
    抛出错误 Promise.reject(error)

三、关键特性

  1. 执行顺序示例

    javascript 复制代码
    console.log("Start");
    
    async function demo() {
      console.log("Async start");
      await new Promise(r => setTimeout(r, 1000));
      console.log("Async end");
    }
    
    demo();
    console.log("End");
    
    // 输出顺序:
    // Start → Async start → End → (1秒后) Async end
  2. 并行执行优化

    javascript 复制代码
    // 顺序执行 (慢)
    const user = await fetchUser();
    const posts = await fetchPosts();
    
    // 并行执行 (快)
    const [user, posts] = await Promise.all([
      fetchUser(),
      fetchPosts()
    ]);
  3. 顶层 await (ES2022):

    javascript 复制代码
    // 模块中直接使用
    const data = await fetchConfig();
    export default data;

四、使用场景

  1. 顺序异步操作(如 API 链式调用)

  2. 错误处理复杂的异步流程

  3. 替代 Promise 链式调用:

    javascript 复制代码
    // Promise 版本
    fetchData()
      .then(process)
      .then(save)
      .catch(handleError);
    
    // async/await 版本
    try {
      const data = await fetchData();
      const processed = await process(data);
      await save(processed);
    } catch (err) {
      handleError(err);
    }

五、注意事项

  1. 避免阻塞陷阱

    javascript 复制代码
    // 错误示例 (意外阻塞)
    async function processBatch() {
      items.forEach(async item => {
        await processItem(item); // 不会并行执行
      });
    }
    
    // 正确做法
    async function processBatch() {
      await Promise.all(items.map(processItem));
    }
  2. 浏览器兼容性

    • 现代浏览器和 Node.js 12+ 完全支持
    • 旧环境需通过 Babel 转译
  3. 性能影响

    • 比原生 Promise 稍慢(约 10%)
    • 可读性优势通常大于性能差异

async/await 让异步代码获得同步代码的可读性,同时保持非阻塞特性,是现代 JavaScript 异步编程的首选方案。





🤔 参考 11

JavaScript 的 async / await 详解


一、核心概念与语法

  1. async 函数

    • 定义 :通过 async function 声明,隐式返回一个 Promise 对象。
    • 返回值
      • 若返回普通值 → Promise 以该值 fulfilled
      • 若抛出异常 → Promise 以错误 rejected
    javascript 复制代码
    async function fetchData() {
      return "数据";
    }
    // 等价于:
    function fetchData() {
      return Promise.resolve("数据");
    }
  2. await 关键字

    • 作用 :暂停 async 函数的执行,等待 Promise 完成并返回结果。
    • 限制 :只能在 async 函数或模块顶级作用域中使用。
    • 返回值
      • Promise 成功 → 返回解析值。
      • Promise 失败 → 抛出异常,需通过 try...catch 捕获。
    javascript 复制代码
    async function getData() {
      const data = await fetch("https://api.example.com/data");
      return data.json();
    }

二、执行流程与底层机制

  1. 同步代码风格
    async/await 使异步代码结构接近同步代码,提升可读性。例如:

    javascript 复制代码
    async function sequential() {
      const data1 = await step1(); // 等待 step1 完成
      const data2 = await step2(data1); // 等待 step2 完成
      return data2;
    }
  2. 事件循环与微任务

    • await 后的代码会被包装为 微任务(Microtask),在 Promise 解决后执行。

    • 示例:

      javascript 复制代码
      console.log("Start");
      async function test() {
        console.log("Async 开始");
        await new Promise(resolve => setTimeout(resolve, 100));
        console.log("Async 结束");
      }
      test();
      console.log("End");
      // 输出顺序:Start → Async 开始 → End → Async 结束
  3. Promise 的等效转换

    javascript 复制代码
    // 原始 Promise 链式调用
    fetchData()
      .then(data => processData(data))
      .catch(error => handleError(error));
    
    // async/await 改写
    async function handle() {
      try {
        const data = await fetchData();
        const result = await processData(data);
        return result;
      } catch (error) {
        handleError(error);
      }
    }

三、错误处理

  1. try...catch 捕获

    • async 函数内使用 try...catch 捕获 await 的异常。
    javascript 复制代码
    async function fetchData() {
      try {
        const response = await fetch("https://api.example.com/data");
        if (!response.ok) throw new Error("HTTP错误");
        return response.json();
      } catch (error) {
        console.error("请求失败:", error);
        return null;
      }
    }
  2. 外部 .catch() 处理

    • 调用 async 函数时,通过 .catch() 捕获返回的 Promise 错误。
    javascript 复制代码
    fetchData()
      .then(data => console.log(data))
      .catch(error => console.error(error));
  3. 未捕获的 Promise 拒绝

    • 若未处理错误,会导致 UnhandledPromiseRejectionWarning
    • 解决方法:全局监听 unhandledrejection 事件(浏览器)或 process.on('unhandledRejection')(Node.js)。

四、高级用法

  1. 并行执行异步操作

    • 使用 Promise.all 结合 await 实现并行:
    javascript 复制代码
    async function fetchMultiple() {
      try {
        const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
        return { user, posts };
      } catch (error) {
        console.error("请求失败:", error);
      }
    }
  2. 链式调用与值传递

    • 每个 await 的返回值作为下一个异步操作的输入:
    javascript 复制代码
    async function processData() {
      const rawData = await fetchRawData();
      const parsedData = await parseData(rawData);
      return savedData(parsedData);
    }
  3. 返回 Promise 的函数

    • await 可等待任何返回 Promise 的表达式,包括普通函数:
    javascript 复制代码
    function delay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }
    async function example() {
      await delay(1000); // 等待 1 秒
      console.log("延迟执行");
    }

五、与 Promise 的对比

特性 Promise async/await
语法 链式调用 .then().catch() 同步代码风格,使用 try...catch
错误处理 需在 .catch() 中集中处理 通过 try...catch 捕获更直观
调试 堆栈信息复杂,难以定位 错误位置明确,调试更简单
并行处理 需手动使用 Promise.all() 结合 Promise.all() 更简洁

六、实际应用场景

  1. 网络请求

    javascript 复制代码
    async function getUserData(userId) {
      try {
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) throw new Error("用户不存在");
        return response.json();
      } catch (error) {
        console.error("请求失败:", error);
        return null;
      }
    }
  2. 文件读写(Node.js)

    javascript 复制代码
    const fs = require('fs').promises;
    
    async function readFileContent(path) {
      try {
        const data = await fs.readFile(path, 'utf-8');
        return data;
      } catch (error) {
        console.error("读取文件失败:", error);
      }
    }
  3. 数据库操作

    javascript 复制代码
    async function getUserById(userId) {
      try {
        const client = await MongoClient.connect(uri);
        const db = client.db("mydb");
        const user = await db.collection("users").findOne({ _id: userId });
        client.close();
        return user;
      } catch (error) {
        console.error("数据库错误:", error);
      }
    }

七、注意事项与最佳实践

  1. 避免过度使用 await

    • 顺序执行异步操作会降低性能,尽量使用 Promise.all 并行处理。
    javascript 复制代码
    // 不推荐:顺序执行
    await step1();
    await step2();
    
    // 推荐:并行执行
    await Promise.all([step1(), step2()]);
  2. 错误处理策略

    • 在关键路径使用 try...catch,非关键路径允许错误冒泡。
    • 提供默认值或降级方案:
    javascript 复制代码
    async function fetchData() {
      try {
        const data = await fetch("/api/data");
        return data.json();
      } catch (error) {
        return { data: "默认值" }; // 降级处理
      }
    }
  3. 调试技巧

    • 使用 console.log 或调试工具追踪异步流程。
    • 注意 await 后的微任务调度,避免逻辑错乱。

八、底层原理

  1. 生成器与协程

    • async/await 基于生成器的暂停-恢复机制,通过协程管理异步流程。
    • 生成器函数通过 yield 暂停执行,async 函数通过 await 实现类似效果。
  2. Promise 的封装

    • async 函数返回的 Promise 由函数体内部的 return 或抛出异常决定状态。
    • await 表达式会创建一个新的 Promise,并在原 Promise 解决后恢复执行。

总结

  • async/await 的核心价值:将异步代码转换为同步风格,提升可读性和维护性。
  • 错误处理 :优先使用 try...catch 集中式捕获,结合 .catch() 实现链式处理。
  • 性能优化 :合理使用 Promise.all 减少等待时间,避免阻塞主线程。

通过掌握 async/await 的机制,可以更高效地编写非阻塞的异步代码,尤其适用于复杂的前后端交互场景。





🤔 参考 12

在 JavaScript 中,async/await 是处理异步操作的革命性语法,它能让你用写同步代码的方式处理异步逻辑。以下是从底层原理到实际应用的全方位解析:


一、核心机制

1. async 关键字

  • 作用:声明一个异步函数
  • 强制返回值:总是返回 Promise 对象
javascript 复制代码
async function basic() {
  return 42; // 等价于 return Promise.resolve(42)
}
// 执行结果
basic().then(console.log); // 输出 42

2. await 关键字

  • 作用域 :只能在 async 函数内部使用
  • 暂停执行:直到右侧的 Promise 进入决断状态
javascript 复制代码
async function fetchData() {
  const response = await fetch('/api'); // 等待请求完成
  const data = await response.json(); // 等待数据解析
  return data;
}

二、与传统 Promise 链的对比

案例:多步骤异步操作

javascript 复制代码
// 传统 Promise 链式写法
function oldWay() {
  return fetchData()
    .then(validate)
    .then(process)
    .catch(handleError);
}

// async/await 写法
async function newWay() {
  try {
    const raw = await fetchData();
    const valid = await validate(raw);
    return process(valid);
  } catch (err) {
    handleError(err);
  }
}

对比优势:

  1. 线性代码结构:消除回调嵌套
  2. 同步错误处理 :使用 try/catch 替代 .catch()
  3. 调试友好:调试器能正确追踪调用栈

三、执行控制策略

1. 并行加速技巧

javascript 复制代码
// 顺序执行(总耗时 = 各任务耗时之和)
async function serial() {
  const a = await task(100);
  const b = await task(200);
  return a + b; // 总耗时 300ms
}

// 并行执行(总耗时 ≈ 最慢任务耗时)
async function parallel() {
  const [a, b] = await Promise.all([task(100), task(200)]);
  return a + b; // 总耗时 200ms
}

2. 循环中的注意事项

javascript 复制代码
// 错误示范:意外串行执行
async function wrongLoop() {
  const arr = [100, 200, 300];
  arr.forEach(async (time) => { // ❌ forEach 不会等待
    await delay(time);
  });
}

// 正确做法:显式控制循环
async function correctLoop() {
  const arr = [100, 200, 300];
  for (const time of arr) { // ✅ 等待每次迭代
    await delay(time);
  }
}

四、错误处理模式

1. 基本错误捕获

javascript 复制代码
async function safeFetch() {
  try {
    const res = await fetch('...');
    return await res.json();
  } catch (err) {
    console.error('请求失败:', err);
    throw new Error('降级数据'); // 继续向上抛出
  }
}

2. 高阶错误处理封装

javascript 复制代码
// 封装可复用的错误处理器
function withRetry(asyncFunc, retries = 3) {
  return async (...args) => {
    for (let i = 0; i < retries; i++) {
      try {
        return await asyncFunc(...args);
      } catch (err) {
        if (i === retries - 1) throw err;
        await delay(1000 * (i + 1));
      }
    }
  };
}

// 使用示例
const reliableFetch = withRetry(fetch);

五、底层原理透析

1. 生成器模拟实现 async/await 本质上是生成器(Generator)的语法糖:

javascript 复制代码
function _async(fn) {
  return function () {
    const gen = fn.apply(this, arguments);
    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let result;
        try {
          result = gen[key](arg);
        } catch (err) {
          return reject(err);
        }
        const { value, done } = result;
        if (done) return resolve(value);
        return Promise.resolve(value).then(
          val => step("next", val),
          err => step("throw", err)
        );
      }
      step("next");
    });
  };
}

2. 事件循环中的执行顺序

javascript 复制代码
console.log('同步开始');
(async () => {
  console.log('async内部-前');
  await Promise.resolve();
  console.log('async内部-后'); // 进入微任务队列
})();
console.log('同步结束');

/* 输出顺序:
同步开始
async内部-前
同步结束
async内部-后 */

六、性能优化策略

  1. 避免不必要的 await
javascript 复制代码
// ❌ 错误
const data = await getData();
return process(data); 

// ✅ 正确
return getData().then(process); // 减少一次微任务
  1. 合理利用缓存
javascript 复制代码
const cache = new Map();
async function getCachedData(id) {
  if (cache.has(id)) return cache.get(id);
  const data = await fetchData(id);
  cache.set(id, data);
  return data;
}

七、浏览器兼容方案

对于不支持 async/await 的老旧浏览器:

  1. 使用 Babel 进行代码转译
  2. 搭配 regenerator-runtime
  3. 打包时自动注入 polyfill

掌握 async/await 后,你可以:

  • 提升代码可维护性 40% 以上
  • 降低异步代码错误率约 35%
  • 轻松处理复杂异步流程控制




🤔 参考 13

以下是针对 JavaScript 中 asyncawait 的超详细解析,涵盖核心机制、语法糖、典型场景及最佳实践:


🔑 一、核心概念速览

关键词 作用 本质
async 声明一个异步函数,使其返回一个 Promise 函数修饰符
await 暂停函数执行,等待右侧 Promise 的决议结果(fulfilled/rejected) 运算符(仅可在 async 函数内使用)

🧠 二、深度解析与关键规则 ✅ 1. async 函数的特性 🔹 基础行为

  • 始终返回 Promise :无论是否显式使用 returnasync 函数都会隐式返回一个 Promise。
    • 若函数内有 return x,则等同于 Promise.resolve(x)
    • 若无 return,则返回 Promise.resolve(undefined)
  • 错误自动捕获 :若函数内部抛出异常,返回的 Promise 会被拒绝(rejected),原因为该异常。

🌰 示例:

javascript 复制代码
async function fetchData() {
  return "数据"; // 等同于 Promise.resolve("数据")
}

fetchData().then(data => console.log(data)); // 输出 "数据"

async function throwError() {
  throw new Error("出错了");
}

throwError().catch(err => console.error(err)); // 输出 Error: 出错了

✅ 2. await 运算符的规则 🔹 核心机制

  • 暂停执行await 会暂停所在 async 函数的执行,直到右侧表达式是一个 Promise 且状态变为 fulfilledrejected
  • 值传递 :若 Promise 成功(fulfilled),await 返回其解析值;若失败(rejected),抛出异常。
  • 非阻塞事件循环:暂停期间释放事件循环,允许其他任务运行。

⚠️ 重要限制:

  • 仅能在 async 函数内使用 :在普通函数或全局作用域中使用 await 会导致语法错误。
  • 右侧必须是 Promise :若右侧是普通值(如字符串、数字),会被自动包装为 Promise.resolve()

🌰 基础示例:

javascript 复制代码
async function main() {
  const result = await Promise.resolve("成功"); // 等待 Promise 解析
  console.log(result); // 输出 "成功"
  
  try {
    const errorResult = await Promise.reject(new Error("失败")); // 抛出异常
  } catch (err) {
    console.error(err); // 输出 Error: 失败
  }
}
main();

⚙️ 三、实战场景与技巧 👨‍💻 场景 1:替代 .then() 链式调用 传统 Promise 风格:

javascript 复制代码
fetch("/api/user")
  .then(response => response.json())
  .then(user => updateUI(user))
  .catch(error => showError(error));

async/await 风格:

javascript 复制代码
async function loadUser() {
  try {
    const response = await fetch("/api/user"); // 等待请求完成
    const user = await response.json();      // 等待 JSON 解析
    updateUI(user);                         // 更新界面
  } catch (error) {
    showError(error);                       // 统一错误处理
  }
}
loadUser();

🛠️ 场景 2:顺序执行多个异步操作

javascript 复制代码
async function processOrder() {
  const paymentStatus = await processPayment(); // 等待支付完成
  const shippingLabel = await generateShippingLabel(paymentStatus); // 根据支付结果生成运单
  const trackingNumber = await sendPackage(shippingLabel); // 发送包裹并获取物流单号
  return trackingNumber;
}

🚀 场景 3:并行执行异步任务

javascript 复制代码
async function parallelTasks() {
  const [user, posts] = await Promise.all([
    fetchUser(),    // 同时发起两个请求
    fetchPosts()
  ]);
  console.log(`用户 ${user.name} 的文章数: ${posts.length}`);
}

🔍 场景 4:条件控制流

javascript 复制代码
async function checkAuth() {
  const token = localStorage.getItem("token");
  if (!token) throw new Error("未登录");
  
  const user = await validateToken(token); // 验证令牌有效性
  return user;
}

四、常见误区与解决方案 ❌ 误区 1:忽略 async 函数的返回值

javascript 复制代码
// 错误写法:未处理 Promise 导致无法获取结果
async function getData() {
  return fetch("/api/data").then(res => res.json());
}
getData(); // 无任何效果

// 正确写法:消费返回的 Promise
getData().then(data => console.log(data));

❌ 误区 2:在全局作用域中使用 await

javascript 复制代码
// 语法错误!await 只能在 async 函数内使用
let data = await fetch("/api/data"); // Uncaught SyntaxError

❌ 误区 3:混淆同步与异步行为

javascript 复制代码
async function printNum() {
  console.log(1);
  await console.log(2); // 宏任务队列化
  console.log(3);      // 微任务队列优先执行
}
printNum();
// 输出顺序:1 → 2 → 3(看似同步,实则异步)

❌ 误区 4:忘记错误处理

javascript 复制代码
// 危险!未捕获的异常会导致未处理的 Promise 拒绝
async function riskyOperation() {
  const data = await fetch("/api/risky"); // 假设请求失败
  return data.json(); // 不会执行到这里
}

// 正确写法:使用 try...catch
async function safeOperation() {
  try {
    const data = await fetch("/api/safe");
    return await data.json();
  } catch (error) {
    console.error("操作失败:", error);
    throw error; // 根据需求决定是否重新抛出
  }
}

💎 五、最佳实践指南

  1. 统一错误处理 :始终使用 try...catch 包裹 await 调用,避免未捕获异常。
  2. 合理拆分函数 :将复杂逻辑拆分为小的 async 函数,提升可测试性和复用性。
  3. 避免过度嵌套 :对于多层嵌套的 await,考虑提取中间变量或使用辅助函数。
  4. 性能优化:对于频繁触发的操作(如滚动事件),结合防抖(debounce)或节流(throttle)技术。
  5. 类型校验 :在 await 后添加类型检查,防止意外的类型转换。

📚 六、关联知识扩展

概念 说明
Promise.all() 等待所有 Promise 完成,任一失败即拒绝
Promise.race() 竞争第一个完成的 Promise,快照决
Promise.allSettled() 等待所有 Promise 结算(无论成功/失败),返回包含状态的对象数组
for...await...of 遍历异步可迭代对象(如异步生成器)

总结async/await 通过同步风格的语法大幅降低了异步代码的复杂度,使代码更易读、更易维护。关键在于理解其底层仍基于 Promise,并遵循错误处理的最佳实践。





🤔 参考 14

asyncawait 是 JavaScript 中用于处理异步操作的一种语法,使得异步代码更像同步代码,提升了可读性和可维护性。

一、async 函数

  • async 是一个关键字,用于声明一个函数是异步函数。
  • 异步函数会自动返回一个 Promise
  • 即使函数没有显式返回 Promise,async 函数也会将返回值包装成一个已解决(resolved)的 Promise。

示例:

javascript 复制代码
async function sayHello() {
  return "Hello";
}

sayHello().then(message => console.log(message)); // 输出 "Hello"

二、await 关键字

  • await 只能在 async 函数内部使用。
  • 它会暂停 async 函数的执行,等待 Promise 完成(即 resolved 或 rejected)。
  • 如果 Promise 成功完成,await 返回其结果;如果失败,会抛出异常。

示例:

javascript 复制代码
async function fetchUser() {
  let response = await fetch('https://api.example.com/user/1');
  let data = await response.json();
  console.log(data);
}

三、错误处理

可以使用 try...catch 来捕获 await 中的异常:

javascript 复制代码
async function fetchUser() {
  try {
    let response = await fetch('https://api.example.com/user/1');
    if (!response.ok) throw new Error("Network response was not ok");
    let data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error fetching user:", error);
  }
}

四、async/await 的优势

  • 代码更清晰,避免了"回调地狱"(callback hell)。
  • 更容易调试和测试。
  • 更接近同步代码的逻辑结构,便于理解和维护。

五、注意事项

  • await 只能在 async 函数中使用。
  • 多个异步任务如果互不依赖 ,可以使用 Promise.all() 来并行执行,提升性能。

示例:并行执行多个异步任务

javascript 复制代码
async function fetchData() {
  let [userRes, postRes] = await Promise.all([
    fetch('https://api.example.com/user/1'),
    fetch('https://api.example.com/posts')
  ]);
  let user = await userRes.json();
  let posts = await postRes.json();
  console.log(user, posts);
}

总结

  • async 函数返回 Promise。
  • await 等待 Promise 完成。
  • 使用 try...catch 捕获错误。
  • 适合处理异步操作,如网络请求、文件读写等。

如果你还有具体的问题或想了解更高级的用法,欢迎继续提问!





🤔 参考 15

async/await 是 JavaScript 处理异步操作的语法糖,基于 Promise 实现,旨在让异步代码的书写和阅读更接近同步代码。以下是详细解析:


1. async 函数

  • 作用:声明一个异步函数
  • 返回值 :总是返回一个 Promise 对象
    • 函数内返回普通值 → 自动包装为 resolved Promise
    • 函数内抛出错误 → 返回 rejected Promise
    • 函数内返回 Promise → 直接返回该 Promise
javascript 复制代码
async function foo() {
  return 42; // 等价于 Promise.resolve(42)
}
foo().then(console.log); // 42

async function bar() {
  throw new Error("失败"); // 等价于 Promise.reject(error)
}
bar().catch(console.error); // Error: 失败

2. await 关键字

  • 作用:暂停异步函数执行,等待 Promise 完成
  • 特性
    • 只能在 async 函数内部使用
    • 等待 Promise 状态变为 resolved 后继续执行
    • 返回 Promise 的解决值
    • 遇到 rejected Promise 会抛出异常(可用 try/catch 捕获)
javascript 复制代码
async function demo() {
  const result = await new Promise(resolve => 
    setTimeout(() => resolve("完成"), 1000)
  );
  console.log(result); // 1秒后输出"完成"
  
  try {
    await Promise.reject("出错了");
  } catch (err) {
    console.error(err); // "出错了"
  }
}

3. 错误处理

  • 推荐方式try/catch
  • 替代方式 :在 Promise 后添加 .catch()
javascript 复制代码
// 方式1: try/catch
async function fetchData() {
  try {
    const data = await fetchApi();
  } catch (error) {
    console.error("请求失败", error);
  }
}

// 方式2: catch处理
fetchApi()
  .then(data => {...})
  .catch(error => console.error(error));

4. 执行流程控制 顺序执行(一个接一个)

javascript 复制代码
async function sequential() {
  const r1 = await task1();
  const r2 = await task2(); // 等待task1完成
}

并行执行(同时触发)

javascript 复制代码
async function parallel() {
  const [res1, res2] = await Promise.all([task1(), task2()]);
  
  // 或
  const p1 = task1(); // 立即启动
  const p2 = task2(); // 立即启动
  await p1;
  await p2;
}

5. 关键特性

  1. 非阻塞性await 暂停的是当前 async 函数,不影响主线程

  2. 隐式转换await 会将非 Promise 值转换为 resolved Promise

    javascript 复制代码
    const num = await 100; // 等价于 await Promise.resolve(100)
  3. 栈追踪:比传统 Promise 链有更清晰的错误堆栈


6. 常见误区 错误:在循环中误用顺序执行

javascript 复制代码
// ❌ 低效写法(顺序等待)
for (const url of urls) {
  const res = await fetch(url); // 每次循环等待完成
}

// ✅ 高效并行写法
const promises = urls.map(url => fetch(url));
const results = await Promise.all(promises);

错误:忘记 await

javascript 复制代码
async function saveData() {
  const data = fetchData(); // ❌ 忘记await,data是Promise对象
  console.log(data); // 输出 Promise { <pending> }
}

7. 浏览器兼容性

  • 支持所有现代浏览器(Chrome 55+、Firefox 52+、Safari 10.1+)
  • Node.js 7.6+ 原生支持
  • 旧环境可通过 Babel 转译

总结对比

模式 优点 缺点
async/await 代码线性易读,错误处理直观 必须包裹在 async 函数中
Promise链 无需函数包裹,适合简单场景 链式嵌套复杂时难以阅读
回调函数 兼容性最好 回调地狱,错误处理困难

最佳实践

在复杂异步逻辑中优先使用 async/await + try/catch,结合 Promise.all() 处理并行任务,可使代码既简洁又可维护。





🤔 参考 16

JavaScript 的 async 与 await 详解

核心概念:async/await 的本质

async/await 是 JavaScript 处理异步操作的语法糖,它基于 Promise,但提供了更直观、同步风格的代码结构:

javascript 复制代码
// 传统 Promise
fetchData()
  .then(data => processData(data))
  .then(result => displayResult(result))
  .catch(error => handleError(error));

// async/await 实现
async function fetchAndDisplay() {
  try {
    const data = await fetchData();
    const result = await processData(data);
    displayResult(result);
  } catch (error) {
    handleError(error);
  }
}

一、async 函数详解

1. 基本特性

  • 声明方式 :在函数前添加 async 关键字
  • 返回值 :始终返回 Promise 对象
    • 返回非 Promise 值 → 自动包装为 resolved Promise
    • 抛出错误 → 返回 rejected Promise
    • 返回 Promise → 直接返回该 Promise
javascript 复制代码
async function example() {
  return 42; // 等价于 Promise.resolve(42)
}

async function fail() {
  throw new Error("Oops"); // 等价于 Promise.reject(Error)
}

example().then(console.log); // 42
fail().catch(console.error); // Error: Oops

2. 执行流程特点

  • 同步执行直到遇到第一个 await
  • 遇到 await 暂停执行,交出控制权
  • 当等待的 Promise 解决后恢复执行
javascript 复制代码
console.log("Start");

async function demo() {
  console.log("Before await");
  await new Promise(res => setTimeout(res, 1000));
  console.log("After await");
}

demo();
console.log("End");

/* 输出顺序:
Start
Before await
End
(等待1秒)
After await
*/

二、await 表达式详解

1. 基本用法

  • 只能在 async 函数内部使用
  • 暂停代码执行,等待 Promise 解决
  • 返回 Promise 的解决值
javascript 复制代码
async function getData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
}

2. 等待行为分析

等待对象类型 await 行为
Promise 等待解决/拒绝
非 Promise 直接返回值
thenable 对象 等待其 then 方法解决
抛出错误 触发 catch 块
javascript 复制代码
// 等待非 Promise 值
const value = await 42; // value = 42

// 等待 thenable 对象
const thenable = {
  then(resolve) {
    setTimeout(() => resolve('Done'), 1000);
  }
};
console.log(await thenable); // 1秒后输出 "Done"

三、错误处理机制

1. try/catch 模式(推荐)

javascript 复制代码
async function loadResource() {
  try {
    const response = await fetch('/resource');
    if (!response.ok) {
      throw new Error('Network response was not OK');
    }
    return await response.json();
  } catch (error) {
    console.error('加载失败:', error);
    return { data: [] }; // 返回默认值
  }
}

2. 结合 catch() 方法

javascript 复制代码
async function process() {
  const data = await fetchData().catch(handleError);
  if (!data) return; // 错误处理后的判断
  
  // 正常处理逻辑
}

四、高级使用技巧

1. 并行执行优化

javascript 复制代码
// 顺序执行(慢)
async function sequentialFetch() {
  const user = await fetch('/user');
  const posts = await fetch('/posts');
  return { user, posts };
}

// 并行执行(快)
async function parallelFetch() {
  const [user, posts] = await Promise.all([
    fetch('/user'),
    fetch('/posts')
  ]);
  return { user, posts };
}

2. 循环中的 await

javascript 复制代码
// 顺序处理(每个操作等待前一个完成)
async function processArray(array) {
  for (const item of array) {
    await processItem(item);
  }
}

// 并行处理(所有操作同时开始)
async function processArrayParallel(array) {
  await Promise.all(array.map(item => processItem(item)));
}

3. 立即执行 async 函数

javascript 复制代码
// IIFE 模式
(async () => {
  const data = await fetchData();
  console.log(data);
})();

// 顶层 await(ES2022+,仅模块中可用)
// 在模块顶层直接使用
const data = await fetchData();
console.log(data);

五、常见问题与解决方案

1. 忘记 await

javascript 复制代码
// 错误:返回 Promise 而不是实际值
async function getValue() {
  const promise = fetchData(); // 缺少 await!
  return promise.result; // undefined
}

// 正确
async function getValue() {
  const data = await fetchData();
  return data.result;
}

2. 异常传播

javascript 复制代码
async function task1() {
  await task2(); // task2 的异常会传播到这里
}

async function task2() {
  throw new Error('Task2 failed');
}

task1().catch(e => console.log(e.message)); // 输出: "Task2 failed"

3. 性能优化

javascript 复制代码
// 优化前:不必要的顺序等待
async function unoptimized() {
  const a = await getA(); // 等待A
  const b = await getB(); // 等待B(不依赖A)
  
  return a + b;
}

// 优化后:并行请求
async function optimized() {
  const [a, b] = await Promise.all([
    getA(), 
    getB() // 同时请求
  ]);
  
  return a + b;
}

六、与 Promise 的对比

特性 Promise async/await
代码结构 链式调用 同步风格
错误处理 .catch() 方法 try/catch 语句
可读性 回调嵌套可能复杂 线性流程更清晰
调试 断点跳转困难 可逐行调试
返回值 必须返回 Promise 自动包装为 Promise
流程控制 需要手动链式调用 自动暂停/恢复执行上下文

七、最佳实践指南

  1. 优先使用 try/catch:处理异步错误

    javascript 复制代码
    async function safeOperation() {
      try {
        return await riskyOperation();
      } catch (error) {
        // 错误处理逻辑
      }
    }
  2. 并行独立操作 :使用 Promise.all

    javascript 复制代码
    async function loadDashboard() {
      const [user, settings, notifications] = await Promise.all([
        fetchUser(),
        fetchSettings(),
        fetchNotifications()
      ]);
      // 渲染所有数据
    }
  3. 避免过度顺序化

    javascript 复制代码
    // 不好:不必要的顺序执行
    const a = await getA();
    const b = await getB(); // 不依赖A,应该并行
    
    // 好:独立操作并行执行
    const [a, b] = await Promise.all([getA(), getB()]);
  4. 合理处理并发

    javascript 复制代码
    // 限制并发数
    import pLimit from 'p-limit';
    
    const limit = pLimit(3); // 最大并发数
    async function processItems(items) {
      const promises = items.map(item => 
        limit(() => processItem(item))
      );
      return Promise.all(promises);
    }

八、浏览器与运行环境支持

环境 支持版本
Chrome 55+ (2016)
Firefox 52+ (2017)
Safari 10.1+ (2017)
Edge 15+ (2017)
Node.js 7.6+ (需要 flag)
8.10+ (正式支持)
TypeScript 1.7+ (编译到 ES3/ES5)

总结:async/await 的价值

  1. 代码可读性:将异步代码写成同步形式
  2. 错误处理简化:使用 try/catch 统一处理
  3. 调试友好:支持标准调试流程
  4. 逻辑清晰:避免回调地狱和复杂链式调用
  5. 与现代JS整合:与 Promise、迭代器等无缝协作
javascript 复制代码
// 完整示例:API请求处理
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) throw new Error('API响应错误');
    
    const user = await response.json();
    const [orders, messages] = await Promise.all([
      fetchUserOrders(user.id),
      fetchUserMessages(user.id)
    ]);
    
    return { ...user, orders, messages };
  } catch (error) {
    console.error('获取用户数据失败:', error);
    return null;
  } finally {
    console.log('用户数据请求完成');
  }
}

掌握 async/await 可以显著提升异步代码的质量和开发效率,是现代 JavaScript 开发的必备技能。





🤔 参考 17

在 JavaScript 中,async/await 是处理异步操作的语法糖,它基于 Promise,但让异步代码的书写和阅读更接近同步方式。下面从核心概念到实际使用进行详细解析:


一、核心概念

  1. async 函数

    • 声明方式:在函数前加 async 关键字
    • 始终返回 Promise 对象
      • 函数内返回非 Promise 值 → 自动包装为 resolved Promise
      • 抛出错误 → 返回 rejected Promise
    javascript 复制代码
    async function foo() {
      return 42; // 等价于 Promise.resolve(42)
    }
    foo().then(console.log); // 输出 42
  2. await 表达式

    • 只能在 async 函数内部使用
    • 暂停函数执行 ,等待 Promise 状态变更:
      • Promise 成功 → 返回 resolve 的值
      • Promise 失败 → 抛出异常 (可用 try/catch 捕获)
    javascript 复制代码
    async function bar() {
      const result = await somePromise; // 等待 Promise 完成
      console.log(result);
    }

二、执行流程解析

javascript 复制代码
async function example() {
  console.log("A");
  await delay(1000); // 暂停执行(不阻塞主线程)
  console.log("B");
}
  1. 执行 console.log("A")
  2. 遇到 await
    • 暂停 example 函数执行
    • 执行 delay(1000) 异步操作
    • 将函数剩余部分放入微任务队列
  3. 主线程继续执行其他代码
  4. 1秒后异步操作完成,恢复函数执行,输出 "B"

三、错误处理 使用 try/catch 捕获异常:

javascript 复制代码
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error("请求失败:", error);
    throw error; // 可选择继续抛出错误
  }
}

四、关键特性

  1. 并行优化

    使用 Promise.all() 避免串行等待:

    javascript 复制代码
    async function parallelFetch() {
      const [user, posts] = await Promise.all([
        fetch('/user'),
        fetch('/posts')
      ]);
      // 两个请求并行发送
    }
  2. 顶层 await(ES2022)

    在模块顶层直接使用:

    javascript 复制代码
    // 模块中
    const data = await fetchData();
    console.log("模块加载完成即获取数据");
  3. 非阻塞原理
    await 只是语法暂停点,实际通过事件循环机制:

    javascript 复制代码
    console.log("Start");
    asyncFunc(); // 内含 await
    console.log("End");
    // 输出顺序:Start → End → 异步操作结果

五、与传统 Promise 对比

javascript 复制代码
// Promise 链式调用
function promiseVersion() {
  fetchData()
    .then(data => process(data))
    .then(result => save(result))
    .catch(handleError);
}

// async/await 版本
async function asyncVersion() {
  try {
    const data = await fetchData();
    const result = await process(data);
    await save(result);
  } catch (err) {
    handleError(err);
  }
}

优势:代码扁平化,避免回调地狱,错误处理更集中。


六、注意事项

  1. 避免过度串行

    不必要的顺序等待会降低性能:

    javascript 复制代码
    // ❌ 低效写法
    const a = await getA(); // 先等A完成
    const b = await getB(); // 再等B完成
    
    // ✅ 高效并行
    const [a, b] = await Promise.all([getA(), getB()]);
  2. 循环中的 await

    在循环内使用需谨慎:

    javascript 复制代码
    // 顺序执行(可能低效)
    for (const url of urls) {
      await fetch(url); // 逐个请求
    }
    
    // 并行执行
    const promises = urls.map(url => fetch(url));
    await Promise.all(promises);
  3. 兼容性

    支持所有现代浏览器(IE 除外)和 Node.js 7.6+,可通过 Babel 转译支持旧环境。


七、总结

特性 说明
async 函数 声明异步函数,隐式返回 Promise
await 暂停函数执行,等待 Promise 结果
错误处理 使用 try/catch 替代 .catch()
并行优化 结合 Promise.all() 提升性能
执行机制 基于事件循环,通过微任务队列恢复执行

最佳实践

  • async/await 替代复杂 Promise 链
  • 并行操作优先使用 Promise.all()
  • 始终用 try/catch 处理错误
  • 避免在循环中滥用顺序等待
相关推荐
汪子熙2 小时前
浏览器环境中 window.eval(vOnInit); // csp-ignore-legacy-api 的技术解析与实践意义
前端·javascript
BUG收容所所长2 小时前
🤖 零基础构建本地AI对话机器人:Ollama+React实战指南
前端·javascript·llm
小高0072 小时前
🚀前端异步编程:Promise vs Async/Await,实战对比与应用
前端·javascript·面试
Spider_Man2 小时前
"压"你没商量:性能优化的隐藏彩蛋
javascript·性能优化·node.js
用户87612829073742 小时前
对于通用组件如何获取表单输入,区分表单类型的试验
前端·javascript
Bdygsl3 小时前
前端开发:JavaScript(6)—— 对象
开发语言·javascript·ecmascript
Mintopia4 小时前
AIGC Claude(Anthropic)接入与应用实战:从字节流到智能交互的奇妙旅程
前端·javascript·aigc
Mintopia4 小时前
Next.js 样式魔法指南:CSS Modules 与 Tailwind CSS 实战
前端·javascript·next.js
国家不保护废物4 小时前
跨域问题:从同源策略到JSONP、CORS实战,前端必知必会
前端·javascript·面试