javascript async和await是什么_如何简化异步代码?

async函数一定返回Promise对象——即使return 42也会被包装为Promise.resolve(42),不写return则等价于return Promise.resolve(undefined);await只能在async函数内使用,因其暂停机制依赖async标记的异步上下文。

asyncawait 是 JavaScript 中处理异步操作的语法糖,本质是基于 Promise 的封装。它们不改变异步本质,但让代码写起来像同步一样线性、可读、易调试。


async 函数返回什么?为什么必须用它包裹 await?

async 函数一定返回一个 Promise 对象——哪怕你 return 42,它也会被自动包装成 Promise.resolve(42);如果你没写 return,就等价于 return Promise.resolve(undefined)

await 只能在 async 函数内部使用,因为它的作用是“暂停当前函数执行,等待右侧表达式 resolve 后继续”,而这种暂停机制依赖函数体被运行时识别为异步上下文(即由 async 标记)。

  • 错误写法:
    function bad() {
      const data = await fetch('/api').then(r => r.json()); // ❌ 报错:SyntaxError: await is only valid in async function
    }
  • 正确写法:
    async function good() {
      const response = await fetch('/api');
      const data = await response.json(); // ✅ 在 async 函数内,await 才合法
      return data;
    }

await 等待的到底是什么?非 Promise 值会怎样?

await 后面可以跟任意值,不只是 Promise

立即学习“Java免费学习笔记(深入)”;

  • 如果是 Promise,它会等待该 Promise 进入 fulfilled 状态,并把 resolve 的值赋给左边变量;
  • 如果不是 Promise(比如数字、字符串、对象),await 会立即返回该值,不阻塞也不异步化——它只是“假装等一下”,然后继续执行。

这说明:await 100await Promise.resolve(100) 行为一致,但前者毫无异步意义,纯属冗余。

async function demo() {
  console.log(await 100);        // 立即输出 100
  console.log(await Promise.resolve(200)); // 立即输出 200(微任务队列中 resolve)
  console.log(await Promise.reject('oops')); // ❌ 抛出未捕获异常,除非 try/catch
}

如何避免 await 顺序等待拖慢性能?

连续 await 是串行的,比如先请求用户、再根据用户 ID 请求订单,这是合理串行;但如果两个请求彼此无关(如获取用户 + 获取公告),用串行就浪费了网络并行能力。

  • ❌ 低效串行:
    const user = await fetch('/user');
    const notice = await fetch('/notice'); // 要等 user 完成才发请求
  • ✅ 并行优化:
    const [user, notice] = await Promise.all([
      fetch('/user'),
      fetch('/notice')
    ]);

Promise.all 适合“全部成功才继续”的场景;若某一项失败不能导致整体失败,改用 Promise.allSettled

注意:await 本身不提供并发控制,它只是语法;并发逻辑仍需靠 Promise.allPromise.race 等原语组织。


错误处理为什么必须用 try/catch?Promise.catch 不行吗?

可以,但不推荐混用。因为 await 把异步错误“同步化”了——reject 会直接以异常形式抛出,就像 throw 一样,所以自然该用 try/catch 捕获。

  • ✅ 推荐(清晰、集中、符合直觉):
    async function load() {
      try {
        const res = await fetch('/data');
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return await res.json();
      } catch (err) {
        console.error('加载失败:', err);
        throw err; // 或返回默认值
      }
    }
  • ⚠️ 混用(易漏、难维护):
    fetch('/data')
      .then(r => r.json())
      .catch(err => console.error(err)); // 无法捕获 fetch 自身 network error(需在外层加 catch)

关键点:未被 try/catch 包裹的 await 错误,会变成 rejected Promise,如果调用方没链上 .catch() 或再次 await,就会触发全局 unhandledrejection —— 这类错误在生产环境极难定位。

真正的坑不在语法,而在习惯:把 await 当同步写,却忘了它背后仍是事件循环和微任务调度;一旦漏掉 try/catch、滥用串行、或误以为 await 能“取消”正在运行的 Promise,问题就会悄无声息地堆积。