前端Promise从入门到精通全指南

发布于:2025-06-28 ⋅ 阅读:(12) ⋅ 点赞:(0)

一、Promise 基础概念与核心机制 [citation:1][citation:2][citation:3]

在这里插入图片描述

1. 三大状态与生命周期
  • Pending(待定):初始状态(如 new Promise() 执行时)。
  • Fulfilled(已兑现):操作成功(resolve() 调用后),状态不可逆。
  • Rejected(已拒绝):操作失败(reject() 调用后),状态不可逆。
    面试聚焦:状态不可逆性是 Promise 可靠性的关键,错误回答“Resolved”会被扣分[citation:7]。
2. 基本用法
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    Math.random() > 0.5 ? resolve('成功数据') : reject('失败原因');
  }, 1000);
});

promise
  .then(res => console.log(res))  // 成功处理
  .catch(err => console.error(err)) // 失败处理
  .finally(() => console.log('清理')); // 必执行[citation:3]
3. 关键特性
  • 微任务队列:then/catch/finally 回调进入微任务队列,优先于宏任务(如 setTimeout)执行。
  • 值穿透:then() 接收非函数参数时,结果直接穿透到下一链式调用(如 .then(2) 忽略参数)[citation:6]。

二、链式调用与错误处理进阶 [citation:1][citation:3][citation:5]

1. 链式调用原理
  • 返回普通值:直接作为下一 then 的输入。
  • 返回新 Promise:下一 then 等待该 Promise 状态变更。
step1()
  .then(result => step2(result)) // 返回Promise
  .then(finalResult => console.log(finalResult));
2. 错误处理策略
  • catch 捕获链中任意错误:替代 .then(null, errHandler) 更清晰。
  • 穿透性:链中未处理的错误会一直传递直到被捕获。
fetchData()
  .then(process)
  .catch(err => {  // 捕获fetchData或process中的错误
    console.error(err);
    return fallbackData; // 提供降级数据
  });

三、静态方法深度解析 [citation:3][citation:6]

在这里插入图片描述

面试聚焦:

  • all vs allSettled:all 关注全成功,allSettled 需处理全部结果[citation:7]。
  • race 的竞态陷阱:若首个 Promise 失败,整个 race 直接拒绝,需谨慎处理错误。

四、手写 Promise 核心实现(面试高频) [citation:4]

1. 基础框架:状态管理与异步队列
class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state !== 'pending') return;
      this.state = 'fulfilled';
      this.value = value;
      this.onFulfilledCallbacks.forEach(fn => fn());
    };

    // reject类似实现...
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    } else if (this.state === 'pending') {
      this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
    }
    // 返回新Promise支持链式调用(需实现resolvePromise)...
  }
}
2. 难点:链式调用与 resolvePromise
  • 循环引用检测:若 then 返回自身,直接 reject(new TypeError(‘循环引用’))。
  • 递归解析:处理返回值为 thenable 对象(如另一 Promise)的情况[citation:4]。

五、高频面试题与答案精析 [citation:6][citation:7]

1. 基础题:输出顺序分析
console.log(1);
new Promise(res => {
  console.log(2);
  res();
}).then(() => console.log(3));
console.log(4);

答案:1 → 2 → 4 → 3
解析:executor 同步执行,then 回调进入微任务队列[citation:6]。

2. 陷阱题:状态不可逆性
const p = new Promise((res, rej) => {
  res('ok');
  rej('error'); // 无效!
});
p.catch(err => console.log(err)); // 无输出

答案:仅输出 ok,因 res() 调用后状态已锁定[citation:2]。

3. 场景题:并发控制(3个并行上限)
async function limitConcurrency(urls, max = 3) {
  const pool = new Set();
  for (const url of urls) {
    const task = loadImg(url);
    pool.add(task);
    task.then(() => pool.delete(task));
    if (pool.size >= max) await Promise.race(pool);
  }
  await Promise.allSettled(pool); // 等待剩余任务
}

关键点:动态任务池 + Promise.race 触发新任务[citation:6]。

六、生产环境最佳实践 [citation:3][citation:5]

  1. 避免嵌套:用链式替代深层嵌套(回调地狱)。
  2. 全局错误处理:
window.addEventListener('unhandledrejection', e => {
  e.preventDefault(); 
  reportError(e.reason);
});
  1. 结合 async/await:
async function fetchUser() {
  try {
    const data = await fetch('/api');
    return process(data);
  } catch (err) {
    // 统一错误分类处理
  }
}

附:Promise 知识图谱

在这里插入图片描述



🚀 一、异步请求封装(解决回调地狱)

  1. HTTP 请求封装
    使用 Promise 封装 fetch 或 axios,统一处理成功/失败逻辑,避免多层嵌套:

function request(url, method = 'GET') {
  return new Promise((resolve, reject) => {
    fetch(url, { method })
      .then(response => response.json())
      .then(resolve)
      .catch(reject);
  });
}
// 使用
request('https://api.example.com/data')
  .then(data => console.log(data))
  .catch(error => console.error('请求失败:', error));
面试重点:
- 为什么需要封装?→ 统一错误处理、减少重复代码[citation:1][citation:6]
- 如何避免重复封装?→ 直接使用支持 Promise 的库(如 axios)[citation:3]

⚡ 二、并发控制与任务协调

  1. 并行请求(Promise.all)
    同时发起多个独立请求,全部成功后再处理:

Promise.all([fetchUser(), fetchPosts(), fetchComments()])
  .then(([user, posts, comments]) => renderPage(user, posts, comments))
  .catch(error => showError('部分数据加载失败'));
场景:页面初始化需加载多个资源[citation:6][citation:7]
  1. 竞速场景(Promise.race)
    设置请求超时机制:

const fetchWithTimeout = (url, timeout = 5000) => 
  Promise.race([
    fetch(url),
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('请求超时')), timeout)
    )
  ]);
场景:网络不稳定时避免长时间等待[citation:7][citation:8]
  1. 批量任务结果收集(Promise.allSettled)
    无论成功失败,统一收集结果:

Promise.allSettled(tasks.map(task => task()))
  .then(results => 
    results.forEach(result => 
      result.status === 'fulfilled' 
        ? logSuccess(result.value) 
        : logError(result.reason)
    )
  );
场景:提交多个表单时需独立处理结果[citation:6][citation:7]

🔗 三、复杂流程链式调用

  1. 顺序依赖操作
    下一步操作依赖上一步结果(如登录 → 获取用户信息 → 渲染):

login(user)
  .then(token => getUserInfo(token))
  .then(info => renderDashboard(info))
  .catch(error => redirectToLogin());
优势:取代回调嵌套,逻辑线性化[citation:3][citation:6]
  1. 中间数据处理
    链式调用中传递加工后的数据:

fetchData()
  .then(raw => JSON.parse(raw))
  .then(data => data.filter(item => item.active))
  .then(activeItems => saveToCache(activeItems));

🛡️ 四、错误处理统一管理

  1. 全局异常捕获
    通过 .catch 集中处理链中任意错误:

asyncOperation()
  .then(step1)
  .then(step2)
  .catch(error => {
    notify('操作失败', error);
    throw error; // 继续向上抛出
  });
最佳实践:链式调用末尾必加 .catch[citation:3][citation:9]
  1. 同步异常捕获(Promise.try)
    ES2025 新特性,统一处理同步/异步错误:

Promise.try(() => {
  if (!isValid(input)) throw new Error('非法输入');
  return asyncCall(input);
}).catch(handleError);
优势:避免同步错误被遗漏[citation:4]

📂 五、文件与数据库操作

  1. 文件读取(Node.js)
    封装 fs.readFile 为 Promise 风格:

const readFile = (path) => 
  new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => 
      err ? reject(err) : resolve(data)
    );
  });
场景:配置文件读取、大数据处理[citation:7]
  1. 数据库查询
    统一接口封装(如 MongoDB):

function queryDB(sql) {
  return new Promise((resolve, reject) => 
    db.query(sql, (err, results) => 
      err ? reject(err) : resolve(results)
    )
  );
}

🔄 六、高级应用模式

  1. 异步任务队列
    控制并发数(如最多同时处理 3 个任务):

async function runTasks(tasks, maxConcurrent = 3) {
  const results = [];
  for (const task of tasks) {
    const current = task().then(res => results.push(res));
    if (++count >= maxConcurrent) await Promise.race(running);
  }
  await Promise.all(running); // 等待剩余任务
  return results;
}
场景:批量图片上传、API 限流[citation:7]
  1. 状态管理集成
    在 Vue/React 中管理异步状态:

// Vue 示例
export default {
  async created() {
    this.loading = true;
    try {
      this.data = await fetchData();
    } catch (e) {
      this.error = e;
    } finally {
      this.loading = false;
    }
  }
};

💎 总结:Promise 核心价值与选型对比

在这里插入图片描述

在这里插入图片描述

⚠️ 避坑指南:
- 避免 new Promise 内直接 resolve 同步结果 → 导致时序混乱(用 Promise.resolve 替代)
- 禁止在循环中滥用 async/await → 用 Promise.all 优化性能
- 永远在链式调用末尾添加 .catch → 防止未处理拒绝(Unhandled Rejection)
通过结合 async/await 语法糖,Promise 能进一步简化代码结构,成为现代前端异步编程的核心范式。实际开发中应根据场景灵活选择并发控制策略,并始终贯彻错误优先原则[citation:6][citation:7]。


网站公告

今日签到

点亮在社区的每一天
去签到