Vue基础教程(25)ECMAScript 6语法之Promise 实现:Vue中的Promise魔法:让异步操作像点外卖一样简单!

一、Promise是什么?外卖订单的终极哲学!

想象一下:你点外卖时,老板不会立马把饭塞你手里,而是给你一张小票(Promise)。这小票有三种命运:备餐中(pending)完成(fulfilled)退款(rejected)。而ECMAScript 6的Promise,就是JavaScript世界的“小票管理器”!

为什么Vue需要Promise?
比如在Vue组件中异步获取用户数据:

// 传统回调地狱(看完想砸键盘)
getUserInfo(userId, (user) => {
  getOrders(user.id, (orders) => {
    getLatestOrder(orders[0].id, (order) => {
      this.orderDetail = order // 嵌套到怀疑人生
    }, errorCallback)
  }, errorCallback)
}, errorCallback)

换成Promise后:

getUserInfo(userId)
  .then(user => getOrders(user.id))
  .then(orders => getLatestOrder(orders[0].id))
  .then(order => { this.orderDetail = order })
  .catch(err => console.log('出错啦:', err))

瞬间从“金字塔代码”变成清爽的流水线!这就是Promise的链式调用魔法


二、手撕Promise源码:自己造一个“小票生成器”

Promise的实现核心是状态机 + 回调队列。我们来手写一个迷你版MyPromise:

class MyPromise {
  constructor(executor) {
    this.state = 'pending' // 状态:pending, fulfilled, rejected
    this.value = undefined // 成功值
    this.reason = undefined // 失败原因
    this.onFulfilledCallbacks = [] // 成功回调队列
    this.onRejectedCallbacks = [] // 失败回调队列

    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
        this.onFulfilledCallbacks.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) {
    // then方法必须返回新Promise,实现链式调用
    return new MyPromise((resolve, reject) => {
      const handleFulfilled = () => {
        try {
          const result = onFulfilled(this.value)
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result)
        } catch (err) {
          reject(err)
        }
      }

      const handleRejected = () => {
        try {
          const result = onRejected(this.reason)
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result)
        } catch (err) {
          reject(err)
        }
      }

      if (this.state === 'fulfilled') {
        setTimeout(handleFulfilled, 0) // 模拟微任务队列
      } else if (this.state === 'rejected') {
        setTimeout(handleRejected, 0)
      } else {
        // pending状态时注册回调
        this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0))
        this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0))
      }
    })
  }
}

关键点解析

  1. 状态不可逆:就像外卖订单不能从“已送达”变回“配送中”
  2. 异步执行:用setTimeout模拟微任务,避免阻塞主线程
  3. 链式调用奥秘:then返回新Promise,使.then().then()成为可能

三、Vue + Promise实战:从入门到骚操作

场景1:异步组件加载(Vue Router官方推荐)
// 用Promise实现懒加载,减少首屏压力
const UserDetail = () => import('./UserDetail.vue') // 底层基于Promise

// 手动封装加载状态
const AsyncComponent = () => ({
  component: import('./HeavyComponent.vue'),
  loading: LoadingSpinner, // 加载中显示组件
  error: ErrorComponent,   // 出错时显示
  delay: 200,              // 延迟显示loading
  timeout: 3000            // 超时时间
})
场景2:接口顺序请求(用户信息→订单列表→商品详情)
export default {
  methods: {
    async fetchData() {
      try {
        // 1. 并行请求优化(互不依赖的接口)
        const [user, config] = await Promise.all([
          this.$api.getUser(),
          this.$api.getConfig()
        ])

        // 2. 顺序请求(依赖前一个结果)
        const orders = await this.$api.getOrders(user.id)
        const latestOrder = await this.$api.getOrderDetail(orders[0].id)
        
        this.data = { user, orders, latestOrder }
      } catch (err) {
        this.$toast.error('数据加载失败')
      }
    }
  }
}
场景3:错误处理黑科技——自动重试机制
// 封装带重试功能的请求
function retryRequest(fn, times = 3) {
  return new Promise(async (resolve, reject) => {
    let lastError
    for (let i = 0; i < times; i++) {
      try {
        const result = await fn()
        return resolve(result)
      } catch (err) {
        lastError = err
        console.log(`第${i+1}次尝试失败:`, err)
        if (i < times - 1) await new Promise(r => setTimeout(r, 1000 * i)) // 延迟重试
      }
    }
    reject(lastError)
  })
}

// 在Vue中使用
export default {
  methods: {
    async fetchWithRetry() {
      const data = await retryRequest(() => this.$api.getData(), 5)
      this.data = data
    }
  }
}

四、Promise进阶技巧与避坑指南

1. 内存泄漏陷阱
// 错误示范:未取消的Promise可能导致内存泄漏
created() {
  this.timer = setInterval(async () => {
    this.data = await this.$api.getData() // 组件销毁后请求仍在继续
  }, 5000)
}

// 正确做法:使用取消令牌
beforeDestroy() {
  if (this.cancelToken) this.cancelToken.cancel('组件销毁')
}
2. 并行限制妙用(比如限制同时上传5个文件)
class PromisePool {
  constructor(maxConcurrent) {
    this.max = maxConcurrent
    this.queue = []
    this.running = 0
  }

  add(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({ task, resolve, reject })
      this.run()
    })
  }

  run() {
    while (this.running < this.max && this.queue.length) {
      const { task, resolve, reject } = this.queue.shift()
      this.running++
      task().then(resolve, reject).finally(() => {
        this.running--
        this.run()
      })
    }
  }
}

// Vue中使用
const pool = new PromisePool(3)
files.forEach(file => {
  pool.add(() => this.uploadFile(file))
})

五、总结

Promise不仅是语法糖,更是异步编程范式的革命。在Vue项目中,结合async/await使用Promise,能让复杂异步流变得像同步代码一样直观。记住核心原则:

  • 状态不可逆:就像人生没有后悔药
  • 链式调用:像流水线一样处理任务
  • 错误冒泡:一个catch抓住整个链条的错误

彩蛋:Promise.allSettled() 即使有失败也会返回所有结果,特别适合“部分成功也算成功”的场景哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值