JavaScript高级程序设计笔记 11

第11章 期约与异步函数 — 快速复习笔记

1. 异步编程背景

  • JavaScript 是单线程、基于事件循环的异步模型。
  • 传统异步方式:回调函数(Callback Hell / 金字塔问题)。
  • ES6 引入 Promise(期约),ES2017 引入 async/await

2. 期约(Promise)

2.1 期约基础

  • 期约是一个对象,代表一个异步操作的最终完成(或失败)及其结果值
  • 状态(不可逆):
    • pending(待定):初始状态。
    • fulfilled(兑现/解决):操作成功完成。
    • rejected(拒绝):操作失败。
  • 创建 Promise:new Promise((resolve, reject) => { ... })
    • 调用 resolve(value) 将状态改为 fulfilled。
    • 调用 reject(reason) 将状态改为 rejected,或抛出错误也会触发 reject。
let promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve('done'), 1000);
});

2.2 实例方法

  • then(onFulfilled, onRejected):注册状态变为 fulfilled 或 rejected 时的回调。
    • 返回一个新的 Promise,实现链式调用。
  • catch(onRejected):语法糖,then(null, onRejected)
  • finally(onFinally):无论状态如何都会执行,一般用于清理操作。返回新 Promise,值会穿透。
promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log('done'));

2.3 链式调用与值传递

  • 每个 then / catch 都返回新 Promise,支持链式异步任务。
  • 回调函数的返回值会作为下一个 then 的输入。
  • 如果返回的是 Promise,它会等待其落定后再继续。

2.4 静态方法

方法 说明
Promise.resolve(value) 将 value 转为 resolved promise(如果是 thenable 则会展开)
Promise.reject(reason) 返回 rejected promise
Promise.all(iterable) 全部成功才成功;任一失败则整体失败(拒绝值就是第一个失败原因)
Promise.allSettled(iterable) 等待所有 promise 落定,返回每个 promise 的状态和结果(ES2020)
Promise.race(iterable) 返回最先落定的 promise(无论成功或失败)
Promise.any(iterable) 返回第一个成功的 promise;全部失败才失败(ES2021)
Promise.all([promise1, promise2])
  .then(([res1, res2]) => console.log(res1, res2))
  .catch(err => console.error(err));

2.5 错误处理与未捕获

  • 未处理的 rejected promise 会在控制台报错(unhandledrejection 事件可监听)。
  • 建议始终添加 catchthen 的第二参数来捕获错误。

3. 异步函数(async/await)

3.1 async 关键字

  • 在函数声明前加 asyncasync function foo() {}
  • 异步函数总是返回一个 Promise:返回值会被 Promise.resolve() 包装;如果抛出错误,返回 rejected promise。
async function getData() {
  return 42; // 等价于 Promise.resolve(42)
}
getData().then(console.log); // 42

3.2 await 关键字

  • 只能在 async 函数内部使用(顶层 await 在 ES2022 模块中允许)。
  • await 后跟一个 Promise,会暂停函数执行,等待 Promise 落定后恢复。
  • 如果 Promise 成功,返回 resolved 的值;如果失败,抛出异常(可用 try-catch 捕获)。
async function fetchData() {
  try {
    let result = await fetch('/api');
    let data = await result.json();
    console.log(data);
  } catch (err) {
    console.error('请求失败', err);
  }
}

3.3 与 Promise 的关系

  • async/await 是基于 Promise 的语法糖,使异步代码写起来像同步代码。
  • 等价于 promise.then(...) 的链式调用,但可读性更好。

3.4 错误处理

  • 使用 try/catch 捕获 await 的错误。
  • 若不加 try/catch,错误会使得异步函数返回 rejected promise,可在外层用 catch 捕获。

3.5 并发执行

  • await 会暂停函数,如果多个异步任务无依赖,应并发执行提升性能:
// 错误:顺序执行,总耗时 = 耗时1 + 耗时2
let a = await asyncTask1();
let b = await asyncTask2();

// 正确:并发执行
let [a, b] = await Promise.all([asyncTask1(), asyncTask2()]);

4. 常见模式与最佳实践

4.1 异步任务串行 vs 并行

  • 串行:一个接一个执行(适合有依赖关系)。
  • 并行:使用 Promise.all / Promise.allSettled

4.2 超时控制

function timeoutPromise(promise, ms) {
  const timeout = new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Timeout')), ms)
  );
  return Promise.race([promise, timeout]);
}

4.3 节流/防抖(结合异步)

  • 通常用标志位或取消机制避免重复请求。

5. 常见面试题速查

  1. Promise 有哪几种状态?
    → pending、fulfilled、rejected,状态变化不可逆。

  2. then 方法返回值是什么?
    → 返回一个新的 Promise,可实现链式调用。

  3. 如何让多个 Promise 并发执行并等待全部成功?
    Promise.all

  4. Promise.allPromise.allSettled 的区别?
    all 一失败就 reject;allSettled 等待所有完成,返回每个的状态。

  5. Promise.racePromise.any 的区别?
    race 返回最先落定的(不论成功/失败);any 返回第一个成功的,全失败才 reject。

  6. async/await 相比 Promise 的优势?
    → 语法更同步,可读性好,错误处理使用 try-catch 更自然。

  7. 如何在 async 函数中实现并发请求?
    → 使用 Promise.all 启动所有请求,再 await

  8. 顶层 await 是什么?
    → 在 ES2022 模块中,可以不在 async 函数内直接使用 await,模块会等待该 Promise 完成。

  9. 如何取消一个 Promise?
    → Promise 本身不可取消,但可以使用 AbortController 配合 fetch 或其他方式实现可取消的异步任务。

  10. 未捕获的 rejected promise 会怎样?
    → 触发 unhandledrejection 事件,并在控制台打印错误。建议全局监听或始终添加 catch。

posted @ 2024-04-19 17:31  Li_pk  阅读(12)  评论(0)    收藏  举报