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

async函数本质

是Promise的语法糖,返回Promise对象;await仅在async函数内有效,暂停当前函数但不阻塞主线程;多请求需用Promise.all并发;错误必须用try/catch显式处理。

async 函数本质是语法糖,不是新机制

它只是 Promise 的封装层,底层仍依赖 Promise 状态流转。声明一个 async 函数,等价于返回一个自动 resolve 的 Promise

async function foo() {
  return 'done';
}
// 等价于
function foo() {
  return Promise.resolve('done');
}

这意味着:即使函数体里没写 await,调用它仍会返回 Promise;你不能直接拿到返回值,必须用 .then() 或外层再套 await

常见误操作:const res = foo() 拿到的是 Promise 对象,不是字符串 'done' —— 这是初学者最常卡住的地方。

await 只能在 async 函数内部使用

await 不是全局关键字,它依赖 async 函数提供的执行上下文。在普通函数、事件回调、setTimeout 回调里直接写 await 会报错:SyntaxError: await is only valid in async functions

  • ✅ 正确:在 async 函数中 await fetch('/api')
  • ❌ 错误:在 document.addEventListener('click', () => { await getData(); }) 中直接 await
  • ✅ 替代方案:把事件回调本身声明为 async,或改用 .then()

注意:箭头函数也能 async,但必须显式标注:const handler = async () => { await apiCall(); }

await 会暂停当前 async 函数,但不阻塞主线程

这是关键理解点。await 暂停的是当前 async 函数的执行流(类似“让出控制权”),JS 引擎会继续处理其他任务(如渲染、其他事件),等 Promise settle 后再恢复该函数。

所以以下代码不会卡死页面:

async function loadAndRender() {
  const data = await fetch('/data').then(r => r.json());
  document.body.innerHTML = JSON.stringify(data);
}

但要注意顺序陷阱:

  • 多个 await 是串行的:后一个要等前一个完成才开始
  • 想并发执行?得用 Promise.all([p1, p2]) 包裹,再 await
  • await Promise.all([fetch('/a'), fetch('/b')]) 比两个单独 await 快得多

错误处理必须显式做,try/catch 不是可选的

await 遇到 rejected 的 Promise 会直接抛出异常,不加 try/catch 就会变成未捕获异常(unhandled rejection),可能触发 unhandledrejection 事件甚至崩溃。

不要这样写:

async function bad() {
  const res = await fetch('/fail'); // 404 时这里就 throw
  return res.json();
}

应该:

async function good() {
  try {
    const res = await fetch('/fail');
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (err) {
    console.error('API failed:', err);
    return null;
  }
}

容易被忽略的一点:被 catch 捕获的错误,**不是原始 Promise reject 的值本身**,而是它包装后的 Error 实例(尤其在 fetch 场景下,需手动检查 res.ok)。