🔥当异步操作遇上俄罗斯套娃式回调,恭喜你解锁「代码屎山」成就!Promise就是来拯救这个世界的超人!

127 阅读3分钟
---
title: 「Promise完全指南」从青铜到王者的异步征服之路|手撕源码+花式操作+防坑秘籍
date: 2023-08-20
tags: [JavaScript, 异步编程, 前端进阶]
cover: https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/45b3d7f3c9c94b1eb5d6d7f4a5a0d7d4~tplv-k3u1fbpfcp-watermark.image?
---

# 🔥 你以为懂Promise?这些骚操作绝对让你瞳孔地震!

「面试造火箭,工作拧螺丝?」今天我们要造的这颗Promise火箭,保证让你在异步编程的宇宙中自由穿梭!准备好迎接认知颠覆了吗?

## 一、青铜局:Promise的十万个为什么

### 1.1 人类迷惑行为大赏
```javascript
// 经典回调地狱
getUser(userId, function(user) {
  getOrders(user.id, function(orders) {
    getItems(orders[0].id, function(items) {
      // 更多嵌套...
    });
  });
});

当异步操作遇上俄罗斯套娃式回调,恭喜你解锁「代码屎山」成就!Promise就是来拯救这个世界的超人!

1.2 Promise的三重人格

const promise = new Promise((resolve, reject) => {
  // 同步执行的执行器函数
  if (/* 成功 */) {
    resolve(value)
  } else {
    reject(reason)
  }
})

// 状态不可逆的奥秘
promise
  .then(value => { /* 成功回调 */ })
  .catch(error => { /* 失败回调 */ })
  .finally(() => { /* 终极回调 */ })

三个神秘状态:pending(等待)、fulfilled(成功)、rejected(失败)。就像薛定谔的猫,一旦确定就永不改变!

二、钻石局:手撕Promise源码(高能预警)

2.1 极简版实现

class MyPromise {
  constructor(executor) {
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onResolvedCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value)
    } else if (this.state === 'rejected') {
      onRejected(this.reason)
    } else {
      this.onResolvedCallbacks.push(() => onFulfilled(this.value))
      this.onRejectedCallbacks.push(() => onRejected(this.reason))
    }
  }
}

⚠️ 注意:这只是一个教学用简化版,真实实现要考虑更多边界条件!

2.2 链式调用的魔法

then(onFulfilled, onRejected) {
  // 返回新Promise实现链式调用
  return new MyPromise((resolve, reject) => {
    const handle = (fn, arg) => {
      try {
        const result = fn(arg)
        // 处理返回值类型
        if (result instanceof MyPromise) {
          result.then(resolve, reject)
        } else {
          resolve(result)
        }
      } catch (error) {
        reject(error)
      }
    }

    // ...省略状态判断
  })
}

链式调用的核心秘密:每次then都返回新的Promise对象!

三、星耀局:Promise全家桶的花式操作

3.1 四大天王对比实验

方法特点成功条件返回值
Promise.all全部成功或任意失败所有promise成功结果数组
Promise.race速度竞赛第一个状态改变第一个结果
Promise.any至少一个成功任一promise成功第一个成功结果
allSettled无论成败全记录所有promise完成状态结果对象数组

3.2 超实用代码配方

// 超时控制
function withTimeout(promise, timeout) {
  return Promise.race([
    promise,
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('Timeout')), timeout)
    )
  ])
}

// 进度条监控
function trackProgress(promises) {
  let completed = 0
  promises.forEach(p => {
    p.finally(() => {
      console.log(`进度:${++completed}/${promises.length}`)
    })
  })
  return Promise.all(promises)
}

四、王者局:深藏不露的防坑秘籍

4.1 死亡陷阱TOP3

  1. 回调地狱转移术(伪链式调用)
// 错误示范!
promise.then(result => {
  promise.then(...) // 形成新的回调地狱
})
  1. 吞没异常的黑洞
promise.then(() => {
  throw new Error('消失的异常') // 未被捕获!
}).catch(() => { /* 这里也捕获不到! */ })
  1. 同步异步的量子纠缠
console.log('开始')
Promise.resolve().then(() => console.log('微任务'))
setTimeout(() => console.log('宏任务'), 0)
console.log('结束')
// 输出顺序:开始 → 结束 → 微任务 → 宏任务

4.2 终极最佳实践

  • 永远返回promise链,避免中断
  • 使用async/await时别忘try/catch
  • 合理使用Promise.all提升性能
  • 避免在Promise中执行阻塞操作

五、神级扩展:通往async/await的虫洞

async function fetchData() {
  try {
    const user = await fetchUser()
    const orders = await fetchOrders(user.id)
    return processData(orders)
  } catch (error) {
    // 统一错误处理
    showErrorToast(error)
  } finally {
    hideLoading()
  }
}

async/await本质是Promise的语法糖,但要注意:

  • await只能在async函数中使用
  • 并行请求仍需配合Promise.all
  • 错误处理要全面